/*
   Declaration of variables
 */


var zoom	= 1;	// Variable that defines the zoom that the canvas will 
// have.

// Drawing elements

var context	= null;	// Temporal context
var contexto	= null;	// Context for the shared canvas (final context)
var canvas	= null;	// Temporal canvas
var canvaso	= null;	// Shared canvas (final canvas)
var textCanvas	= null;	// Canvas used exclusively for text

// Mouse position elements

var posX	= 0;	// Mouse position on the X-axis (current position).
var posY	= 0;	// Mouse position on the Y-axis (current position).
var posXIni	= 0;	// Mouse position on the X-axis for an initialized trajectory.
// action.
var posYIni	= 0;	// Mouse position on the X-axis for an initialized trajectory.
// action.

// Helper variables for the general Gadget operation

var option	= '1';	// Variable used for editing purposes. It's required
// to identify which operation the gadget will do
// when it receives an event.

var firstLoaded	= 1; // Variable to determine when the gadget loads for the
// first time.

// Variables for the size of objects.

var entityHeight;	// Variable that determines the width of an entity.
var entityWidth;	// Variable that determines the height of an entity.

var weakEntityHeight;	// Variable that determines the width of a weak entity.
var weakEntityWidth;	// Variable that determines the height of a weak entity.

var relationHeight;	// Variable that determines the width of an relation
var relationWidth;	// Variable that determines the height of an relation

var canvasWidth	= 2000;	// Variable that determines the width of the canvas
var canvasHeight = 1600;	// Variable that determines the height of the 
// canvas

// Variables to store HTML elements of the Gadget

var canvasDiv;	// Div where the drawing canvases are located
var indicationsDiv;	// Div where the indications are displayed. These change
// according to what the user is doing.
var usersDiv;	// Div that shows the users that are working on the diagram
// and their corresponding colors.


var canEdit	= 0;	// Variable that defines whether you can edit the
// diagram or not.

// Helper variables for the Line tool.

var drawingLine	= 0;	// Variable that determines whether the user is
//currently drawing a line or not.
var firstLineSelection	= "";	// Variable that determines which was the
// first element selected when the line
// was started.

// Helper variables for the movement tool

var moving	= 0;	// Variable that determines whether the user is moving
// an object
var objectsBeingMoved	= null;	// Variable that will store an array with
// the objects currently being moved.
var movingLimits	= null;	// Variable that will store an array with the
// objects which position defines the edges of
// the selection. This is done so that the user
// doesn't move elements outside of the 
// boundaries of the drawing canvas.
var movementDelta	= null;	// Varible that will store an array containing
// the X and Y axis delta's of the movement. In
// other words, it will store how much the
// objects where moved.

// Helper variables for the selection tool.

var drawingSelectionBox	= false;	// Variable that defines whether the 
// user is drawing a selection box or 
// not.

// User colors
// These arrays are used to get the colors that belong to the users
// that can draw on the canvas, and so the color will be used to show
// who selected an object.
var colors	= ['red', 'blue', 'green', 'yellow', 'orange', 'purple',
    'aqua'];
var colorsHex	= ['#FF0000', '#0000FF', '#008000', '#FFEF00', 
    '#FF7F00','#FF7F00','#00FFFF'];
//red blue green yellow orange purple aqua

var hasNewName = 0;		// Variable that serves as a boolean flag to tell when
// a recently created object acquired a new name.
var hasNewParticipation = 0;	// Variable that serves as a boolean flag to tell when
// a recently created line between a relation
// and an entity acquired a new participation value.

var canDraw = 1; // Variale that prevents the gadget from adding two objects when
// double-clicking.

/*
   Initialization Function
 */

/*
 * Function that executes the canvas initialization:
 * the size of the objects and the link between the objects and their
 * functions (e.g.: mousedown).
 * The function is called at the beginning of the Gadget's execution by 
 * init().
 * Beginning: The gadget has no context to draw on the canvas, and no link
 * between the events and the event handlers.
 * End: The gadget has defined event handlers and the context was created.
 */
function initialize() {

	canvasDiv	= document.getElementById('contentDiv');
	indicationsDiv	= document.getElementById('indications');
	usersDiv	= document.getElementById('users');

	canvaso	= document.getElementById('imageView');
	textCanvas	= document.getElementById('textCanvas');

	if(!canvaso) {
		alert('Error: I cannot find the canvas element!(imageView)');
		return;
	}

	if(!textCanvas) {
		alert('Error: I cannot find the canvas element!(textCanvas)');
		return;
	}

	if (!canvaso.getContext) {
		alert('Error: no canvas.getContext!');
		return;
	}

	if(!textCanvas.getContext) {
		alert('Error: no textCanvas.getContext!');
		return;
	}

	// Get the 2D canvas context.
	contexto	= canvaso.getContext('2d');
	if (!contexto) {
		alert('Error: failed to getContext!');
		return;
	}

	textContext	= textCanvas.getContext('2d');
	if (!textContext) {
		alert('Error: failed to getContext!');
		return;
	}

	// Add the temporary canvas.
	var container	= canvaso.parentNode;
	canvas	= document.createElement('canvas');
	if (!canvas) {
		alert('Error: I cannot create a new canvas element!');
		return;
	}

	canvas.id	= 'imageTemp';
	canvaso.width	= canvasWidth;
	canvaso.height	= canvasHeight;
	canvas.width	= canvaso.width;
	canvas.height	= canvaso.height;
	textCanvas.width	= canvasWidth;
	textCanvas.height	= canvasHeight;
	canvas.tabindex	= 1;
	container.appendChild(canvas);

	entityHeight = canvas.height * .04;
	entityWidth = canvas.width * .05;

	weakEntityHeight = canvas.height * .04;
	weakEntityWidth	= canvas.width * .05;

	relationHeight = canvas.height * .05;
	relationWidth = canvas.width * .07;

	attributeRadius = canvas.height * .006;
	attributeRadius = canvas.height * .006;

	speGenRadius = canvas.height * .012;
	speGenRadius = canvas.height * .012;

	context	= canvas.getContext('2d');

	context.strokeStyle	= 'grey';
	contexto.fillStyle	= '#FFFFFF';	//white

	textContext.textAlign	= "center";
	textContext.font	= "12px sans-serif";
	textContext.fillStyle	= "#000";

	// Add event handlers
	canvas.addEventListener('mousedown', mouseDown, false);
	canvas.addEventListener('mousemove', mouseMove, false);
	canvas.addEventListener('mouseup', mouseUp, false);
	canvas.addEventListener('dblclick', dblClick, false);

}

/*
   General Gadget Operation Functions
 */

/*
 * Function that initialices the execution of various tools. It receives a
 * number that should match the tool's option.
 * Input: A number matching the desired option.
 * Beginning: The gadget may or not be previously used by another tool, and
 * so, it's helper variables will have the values that helped that tool.
 * Also, the indications correspond to the previous tool's indications.
 * End: The helper variables are re-initialized and new indications are
 * drawn on the canvas.
 */
function generalControl(op) {

	op	= parseInt(op);
	switch (op) {
		case 1:	// Selection
			option	= '1';
			drawingLine = 0;
			resetMovingElements();
			context.clearRect(0, 0, canvas.width, canvas.height);
			indicationsDiv.innerHTML	= 
				"<label>Ready</label>";
			$('.selected-wave-like-buttons').removeClass(
					'selected-wave-like-buttons'
					).addClass('wave-like-buttons');
			$('#select').removeClass('wave-like-buttons');
			$('#select').addClass('selected-wave-like-buttons');
			break;
		case 2:	// Move object
			option	= '2';
			drawingLine = 0;
			stopSelectionBox();
			context.clearRect(0, 0, canvas.width, canvas.height);
			indicationsDiv.innerHTML	= 
				"<label>Drag the object to the desired position</label>";
			$('.selected-wave-like-buttons').removeClass(
					'selected-wave-like-buttons').addClass('wave-like-buttons'
						);
			$('#move').removeClass('wave-like-buttons');
			$('#move').addClass('selected-wave-like-buttons');
			break;
		case 3:	// Erase
			option	= '3';
			drawingLine = 0;
			stopSelectionBox();
			resetMovingElements();
			context.clearRect(0, 0, canvas.width, canvas.height);
			mouseUpOperations();
			break;
		case 4:	// Draw line
			option	= '4';
			stopSelectionBox();
			resetMovingElements();
			context.clearRect(0, 0, canvas.width, canvas.height);
			indicationsDiv.innerHTML	= 
				"<label>Trace a line between the corresponding entity and relation</label>";
			$('.selected-wave-like-buttons').removeClass(
					'selected-wave-like-buttons').addClass('wave-like-buttons'
						);
			$('#line').removeClass('wave-like-buttons');
			$('#line').addClass('selected-wave-like-buttons');
			break;
		case 5:	// Draw relation
			option	= '5';
			drawingLine = 0;
			stopSelectionBox();
			resetMovingElements();
			context.clearRect(0, 0, canvas.width, canvas.height);
			indicationsDiv.innerHTML	= 
				"<label>Click on the canvas to draw a relation</label>";
			$('.selected-wave-like-buttons').removeClass(
					'selected-wave-like-buttons'
					).addClass('wave-like-buttons');
			$('#relation').removeClass('wave-like-buttons');
			$('#relation').addClass('selected-wave-like-buttons');
			break;
		case 6:	// Draw attribute
			option	= '6';
			drawingLine = 0;
			stopSelectionBox();
			resetMovingElements();
			context.clearRect(0, 0, canvas.width, canvas.height);
			indicationsDiv.innerHTML	= 
				"<label>Click on the canvas to draw an attribute</label>";
			$('.selected-wave-like-buttons').removeClass(
					'selected-wave-like-buttons').addClass('wave-like-buttons'
						);
			$('#attribute').removeClass('wave-like-buttons');
			$('#attribute').addClass('selected-wave-like-buttons');
			break;
		case 7:	// Draw entity
			option	= '7';
			drawingLine = 0;
			stopSelectionBox();
			resetMovingElements();
			context.clearRect(0, 0, canvas.width, canvas.height);
			indicationsDiv.innerHTML	= 
				"<label>Click on the canvas to draw an entity</label>";
			$('.selected-wave-like-buttons').removeClass(
					'selected-wave-like-buttons'
					).addClass('wave-like-buttons');
			$('#entity').removeClass('wave-like-buttons');
			$('#entity').addClass('selected-wave-like-buttons');
			break;
		case 8:	// Draw weak entity
			option	= '8';
			drawingLine = 0;
			stopSelectionBox();
			resetMovingElements();
			context.clearRect(0, 0, canvas.width, canvas.height);
			indicationsDiv.innerHTML	= 
				"<label>Click on the canvas to draw a weak entity</label>";
			$('.selected-wave-like-buttons').removeClass(
					'selected-wave-like-buttons'
					).addClass('wave-like-buttons');
			$('#weakEntity').removeClass('wave-like-buttons');
			$('#weakEntity').addClass('selected-wave-like-buttons');
			break;
		case 9:	// Draw a double line
			option	= '9';
			drawingLine = 0;
			stopSelectionBox();
			resetMovingElements();
			context.clearRect(0, 0, canvas.width, canvas.height);
			indicationsDiv.innerHTML	= 
				"<label>Trace a line between the corresponding entity and relation</label>";
			$('.selected-wave-like-buttons').removeClass(
					'selected-wave-like-buttons'
					).addClass('wave-like-buttons');
			$('#doubleLine').removeClass('wave-like-buttons');
			$('#doubleLine').addClass('selected-wave-like-buttons');
			break;
		case 10:// Draw multivalued attribute
			option	= '10';
			drawingLine = 0;
			stopSelectionBox();
			resetMovingElements();
			context.clearRect(0, 0, canvas.width, canvas.height);
			indicationsDiv.innerHTML	= 
				"<label>Click on the canvas to draw a multivalued attribute</label>";
			$('.selected-wave-like-buttons').removeClass(
					'selected-wave-like-buttons').addClass('wave-like-buttons');
			$('#multiAttribute').removeClass('wave-like-buttons');
			$('#multiAttribute').addClass('selected-wave-like-buttons');
			break;
		case 11:// Draw derived attribute
			option	= '11';
			drawingLine = 0;
			stopSelectionBox();
			resetMovingElements();
			context.clearRect(0, 0, canvas.width, canvas.height);
			indicationsDiv.innerHTML	= 
				"<label>Click on the canvas to draw a derived attribute</label>";
			$('.selected-wave-like-buttons').removeClass(
					'selected-wave-like-buttons').addClass('wave-like-buttons');
			$('#derivedAttribute').removeClass('wave-like-buttons');
			$('#derivedAttribute').addClass('selected-wave-like-buttons');
			break;
		case 12:// Switch to key
			option	= '12';
			drawingLine = 0;
			stopSelectionBox();
			resetMovingElements();
			context.clearRect(0, 0, canvas.width, canvas.height);
			mouseUpOperations();
			$('#switchToKey').removeClass('wave-like-buttons');
			$('#switchToKey').addClass('selected-wave-like-buttons');
			break;
		case 13:// Switch to partial key
			option	= '13';
			drawingLine = 0;
			stopSelectionBox();
			resetMovingElements();
			context.clearRect(0, 0, canvas.width, canvas.height);
			mouseUpOperations();
			$('#switchToPartialKey').removeClass('wave-like-buttons');
			$('#switchToPartialKey').addClass('selected-wave-like-buttons');
			break;
		case 14:// Draw weak relation
			option	= '14';
			drawingLine = 0;
			stopSelectionBox();
			resetMovingElements();
			context.clearRect(0, 0, canvas.width, canvas.height);
			indicationsDiv.innerHTML	= 
				"<label>Click on the canvas to draw a weak relation</label>";
			$('.selected-wave-like-buttons').removeClass(
					'selected-wave-like-buttons'
					).addClass('wave-like-buttons');
			$('#weakRelation').removeClass('wave-like-buttons');
			$('#weakRelation').addClass('selected-wave-like-buttons');
			break;
		case 15:// Remove key
			option = '15';
			drawingLine = 0;
			stopSelectionBox();
			resetMovingElements();
			context.clearRect(0, 0, canvas.width, canvas.height);
			mouseUpOperations();
			$('#removeKey').removeClass('wave-like-buttons');
			$('#removeKey').addClass('selected-wave-like-buttons');
			break;
		case 16:// Specialization/Generalization figure
			option = '16';
			drawingLine = 0;
			stopSelectionBox();
			resetMovingElements();
			context.clearRect(0, 0, canvas.width, canvas.height);
			indicationsDiv.innerHTML	= 
				"<label>Click on the canvas to draw a specialization/generalization figure</label>";
			$('.selected-wave-like-buttons').removeClass(
					'selected-wave-like-buttons'
					).addClass('wave-like-buttons');
			$('#speGen').removeClass('wave-like-buttons');
			$('#speGen').addClass('selected-wave-like-buttons');
			break;
		case 17:// Switch to disjoint spe/gen type
			option = '17';
			drawingLine = 0;
			stopSelectionBox();
			resetMovingElements();
			context.clearRect(0, 0, canvas.width, canvas.height);
			mouseUpOperations();
			$('#disjoint').removeClass('wave-like-buttons');
			$('#disjoint').addClass('selected-wave-like-buttons');
			break;
		case 18:// Switch to overlapping spe/gen type
			option = '18';
			drawingLine = 0;
			stopSelectionBox();
			resetMovingElements();
			context.clearRect(0, 0, canvas.width, canvas.height);
			mouseUpOperations();
			$('#overlapping').removeClass('wave-like-buttons');
			$('#overlapping').addClass('selected-wave-like-buttons');
			break;
		case 19:// Switch to union spe/gen type
			option = '19';
			drawingLine = 0;
			stopSelectionBox();
			resetMovingElements();
			context.clearRect(0, 0, canvas.width, canvas.height);
			mouseUpOperations();
			$('#union').removeClass('wave-like-buttons');
			$('#union').addClass('selected-wave-like-buttons');
			break;
		case 20:// Remove spe/gen type
			option = '20';
			drawingLine = 0;
			stopSelectionBox();
			resetMovingElements();
			context.clearRect(0, 0, canvas.width, canvas.height);
			mouseUpOperations();
			$('#removeSpeGen').removeClass('wave-like-buttons');
			$('#removeSpeGen').addClass('selected-wave-like-buttons');
			break;
		default:
			break;
	}

}

/*
 * The function dblClick is the event handler of the dblClick event for 
 * the temporal canvas.
 */
function dblClick(evt) {
	alert("doble click");
}

/*
 * The function mouseDown is the event handler of the mouseDown event for 
 * the temporal canvas.
 */
function mouseDown(evt) {
	if(wave.getMode()	== wave.Mode.EDIT) {
		switch(option) {
			case '1': // case for the select option
				startSelectionBox();
				break;
			case '2': // case for the movement option
				if(moving	== 0) {
					startMoving();
					$('#imageTemp').css('cursor', 'move');
				}
				break;
			case '4': // case for the line drawing option
				if(drawingLine	== 0)
					startLine(); 
				break;
			case '7': // case for the entity drawing option
				break;
			case '8': // case for the weak entity drawing option
				break;
			case '9': // case for the double line drawing option
				if(drawingLine	== 0)
					startLine(); 
				break;
			default:
				break;
		}
	}

}

/*
 * The mouseUp function is linked to the canvas and calls the
 * mouseUpOperations function. Before this, it must verify that effectively
 * the user is a participant of the team, and has editing permissions. Also,
 * the user must be on Wave's editing mode.
 */

function mouseUp(evt){

	if (context && wave && wave.getState() && 
			wave.getState().get('participants')	&& 
			canEdit != 0 && wave.getMode()	== wave.Mode.EDIT) {

		mouseUpOperations();
	}

}

/*
 * The mouseUpOperatinos is called by mouseUp and calls the functions that
 * belong to the tool that will be used. Before this, it must pass through
 * a critial section (that's a state in the Gadget) to assure the
 * consistency of the Gadget's state in all the blip's instances.
 */
function mouseUpOperations() {

	if(wave.getState().get("CS")	== null ||
			wave.getState().get("CS")	== undefined ||
			wave.getState().get("CS")	== '0' ||
			wave.getState().get("CS")	== ("who:" + wave.getPrivateState().get("myColor"))) {
		// enters the if statement when the critical section belongs
		// only to the local user

		var delta	= {};
		delta['CS']	= "who:" + wave.getPrivateState().get("myColor");
		wave.getState().submitDelta(delta);
		// tells the wave the local user is the one inside the
		// critical section

		if(wave.getState().get('CS')	== ("who:" + wave.getPrivateState().get("myColor"))) {
			switch(option) {
				case '1':	// Selection tool
					stopSelectionBox();

					var a	= Math.pow(posX - posXIni, 2);
					var b	= Math.pow(posY - posYIni, 2);
					var c	= Math.sqrt(a + b);

					if(c < 50.0) {
						selectIndividual();
					} else {
						selectMultiple();
					}
					break;
				case '2':	// Movement tool
					if(moving	== 1) {
						finishMoving();
						resetMovingElements();
						$('#imageTemp').css('cursor', 'default');
						generalControl('0');
					}
					break;
				case '3':	// Erase object(s)
					erase();
					generalControl('1');
					break;
				case '4': // Draw line
					if(drawingLine	== 1)
						drawLine();
					break;
				case '5':	// Draw relation
					drawObject('relation'); 
					break;
				case '6':	// Draw attribute
					drawObject('attribute'); 
					break;
				case '7':	// Draw entity
					drawObject('entity'); 
					break;
				case '8':	// Draw weak entity.
					// NOTE: because option equals 8, the gadget
					// will know it's a weak entity later on.
					drawObject('entity'); 
					break;
				case '9': // Draw double line
					if(drawingLine	== 1)
						drawLine();
					break;
				case '10':	// Draw multivalued attribute
					drawObject('attribute'); 
					break;
				case '11':	// Draw derived attribute
					drawObject('attribute'); 
					break;
				case '12':	// Switch to key
					switchAttrType('12');
					generalControl('1');
					break;
				case '13':	// Switch to partial key
					switchAttrType('13');
					generalControl('1');
					break;
				case '14':	// Draw weak relation
					drawObject('relation');
					break;
				case '15':	// Remove key
					switchAttrType('15');
					generalControl('1');
					break;
				case '16':	// Draw specialization/generalization figure
					drawObject('speGen'); 
					generalControl('1');
					break;
				case '17':	// Switch the spe/gen type to disjoint
					switchSpeGenType('17');
					generalControl('1');
					break;
				case '18':	// Switch the spe/gen type to overlapping
					switchSpeGenType('18');
					generalControl('1');
					break;
				case '19':	// Switch the spe/gen type to union
					switchSpeGenType('19');
					generalControl('1');
					break;
				case '20':	// Switch the spe/gen type to none
					switchSpeGenType('20');
					generalControl('1');
					break;
				default:
					break;
			}
		} else {
			// if someone else is in the critical section
			var timeOut	= window.setTimeout('mouseUpOperations()', 300);
			return;
		}

		delta['CS']	= '0';
		wave.getState().submitDelta(delta);

		var timeOutMenu	= window.setTimeout('auxMenuControl()',300);
	} else {
		var timeOut	= window.setTimeout('mouseUpOperations()', 300);
		// If the Critical section doesn't exist, or it's not used by
		// someone, or it's yours, enter the if. Else try again in
		// 300 milliseconds.
		return;
	}
}

/*
 * The mouseMove function is linked to the drawing canvas and executes
 * a specific action depending on the option that's selected.
 * In most cases, the action that's taken is used to make a temporal drawing
 * of the tool.
 * e.g: the drawTempEntity function draws a temporal entity (with grey 
 * lines), to show how the entity would look like if it were drawn, but it's
 * not a final entity.
 */
function mouseMove( evt ) {

	if( !evt )
		evt	= event;
	if (evt.layerX || evt.layerX	== 0) { // Firefox
		evt._x	= evt.layerX;
		evt._y	= evt.layerY;
	} else if (evt.offsetX || evt.offsetX	== 0) { // Opera
		evt._x	= evt.offsetX;
		evt._y	= evt.offsetY;
	}
	if( wave.getMode()	== wave.Mode.EDIT) {
		posX	= parseInt(evt._x / zoom);
		posY	= parseInt(evt._y / zoom);

		if(posX < 1) {
			posX = 1;
		}

		if(posY < 1) {
			posY = 1;
		}

		switch(option) {
			case '1':	// Selection tool
				if(drawingSelectionBox) {
					drawSelectionBox();
				}
				break;
			case '2':	// Movement tool
				if(moving	== 1) {
					whileMoving();
				}
				break;
			case '4':	// Draw temporal line
				if(drawingLine	== 1) {
					drawTempLine();
				}
				break;
			case '5':	// Draw temporal relation
				drawTempRelation();
				break;
			case '6':	// Draw temporal attribute
				drawTempAttribute();
				break;
			case '7':	// Draw temporal entity
				drawTempEntity();
				break;
			case '8':	// Draw temporal weak entity
				drawTempEntity();
				break;
			case '9':	// Draw temporal double line
				if(drawingLine	== 1) {
					drawTempLine();
				}
				break;
			case '10':	// Draw temporal multivalued attribute
				drawTempAttribute();
				break;
			case '11':	// Draw temporal derived attribute
				drawTempAttribute();
				break;
			case '14':	// Draw temporal weak relation
				drawTempRelation();
				break;
			case '16':	// Draw temporal specialization/generalization
				drawTempSpeGen();
				break;
			default:
				break;
		}
	}

}

/*
   Auxiliary Menu Functions
 */

/*
 * Function that works as a helper for the functionality of the dynamic
 * menu which shows the options to: insert an attribute for the selected
 * object, erase the selected object(s) or move the selected object(s).
 * It uses JQuery's help for the effects it uses.
 * Beginning: The dynamic menu is or is not shown on the user's screen.
 * End: According to the user's interaction with the gadget, the dynamic
 * menu will appear or disappear. If the user selected an object, the 
 * dynamic menu will appear or re-appear, showing the options available
 * for the user (whether it's one object that was selected, or more, the
 * options vary). If nothing was selected or the "hide" button was pressed
 * the dynamic menu will disappear.
 */
function auxMenuControl() {

	var none_selected = 0;
	var selected = "";

	// function that hides or shows the buttons
	// used to insert various types of attributes
	function attributeButtons(action){
		if(action == 'show'){
			$("#attribute").show();
			$("#multiAttribute").show();
			$("#derivedAttribute").show();
		}else if(action == 'hide'){
			$("#attribute").hide();
			$("#multiAttribute").hide();
			$("#derivedAttribute").hide();
		}
	}

	// function that hides or shows the buttons
	// used to insert various types of keys
	function keyButtons(action){
		if(action == 'show'){
			$("#switchToKey").show();
			$("#switchToPartialKey").show();
			$("#removeKey").show();
		}else if(action == 'hide'){
			$("#switchToKey").hide();
			$("#switchToPartialKey").hide();
			$("#removeKey").hide();
		}
	}

	// function that hides or shows the buttons
	// used to insert various types of specialization
	// or generalization figures
	function speGenButtons(action){
		if(action == 'show'){
			$("#disjoint").show();
			$("#overlapping").show();
			$("#union").show();
			$("#removeSpeGen").show();
		}else if(action == 'hide'){
			$("#disjoint").hide();
			$("#overlapping").hide();
			$("#union").hide();
			$("#removeSpeGen").hide();
		}
	}

	if(wave.getState().get('selected') != null && wave.getState().get('selected') != '0') {
		selected = wave.getState().get('selected');
		// if someone else has selected anything and I have not
		if(selected.indexOf("who:" + wave.getPrivateState().get('myColor')) == -1){
			none_selected = 1;
		}else{
			$('#auxMenuWrapper').fadeIn();
			$('#auxMenuSlideButton1').fadeIn();
			$('#auxMenuSlideButton2').fadeOut();
			// if only one object is selected by me
			if(countMatches(selected,"who:" + wave.getPrivateState().get('myColor')) == 1){
				// split everything that has been selected in order to look for
				// what I have selected with a for loop
				var selectedParts = selected.split(",");
				// variable that will contain my single-object selection only
				var selectedByMe;
				for(var x = 0; x < selectedParts.length; x++){
					if(selectedParts[x].indexOf("who:" + wave.getPrivateState().get('myColor')) != -1){
						selectedByMe = selectedParts[x];
					}
				}
				// if there are entities in existence
				if(wave.getState().get('entity') != null && wave.getState().get('entity') != '0'){
					// if I have selected an entity
					if(wave.getState().get('entity').indexOf(selectedByMe.split("_")[0]) > -1){
						attributeButtons('show');
						keyButtons('hide');
						$('#speGen').show();
						speGenButtons('hide');
					}
				}
				// if there are relations in existence
				if(wave.getState().get('relation') != null && wave.getState().get('relation') != '0'){
					// if I have selected a relation
					if(wave.getState().get('relation').indexOf(selectedByMe.split("_")[0]) > -1){
						attributeButtons('show');
						keyButtons('hide');
						$('#speGen').hide();
						speGenButtons('hide');
					}
				}
				// if there are attributes in existence
				if(wave.getState().get('attribute') != null && wave.getState().get('attribute') != '0'){
					// if I have selected an attribute
					if(wave.getState().get('attribute').indexOf(selectedByMe.split("_")[0]) > -1){
						$('#speGen').hide(); // hide the sepGen button
						speGenButtons('hide'); //hide the speGen specific buttons
						attrArr = wave.getState().get('attribute').split(",");
						for(var x = 0; x < attrArr.length; x++){
							attrArrParts = attrArr[x].split("_");
							if(attrArrParts.indexOf(selectedByMe.split("_")[0]) > -1){
								attrPos = attrArrParts.indexOf(selectedByMe.split("_")[0]);
								break;
							}
						}
						// the type of attribute is obtained
						// by adding two positions to that
						// attribute's id position
						var attrType = attrArrParts[attrPos+2];
						// if the attribute is a simple or compound one
						if(attrType != 'type:derived'){
							var keyType = attrArrParts[attrPos+3];
							// depending of which type of key the selected
							// attribute is, some menu buttons will be shown
							// or hidden
							if(keyType == 'key:0'){
								$("#switchToKey").show();
								$("#switchToPartialKey").show();
								$("#removeKey").hide();
							}else if(keyType == 'key:1'){
								$("#switchToKey").hide();
								$("#switchToPartialKey").show();
								$("#removeKey").show();
							}else if(keyType == 'key:2'){
								$("#switchToKey").show();
								$("#switchToPartialKey").hide();
								$("#removeKey").show();
							}
							if(attrType != 'type:multi'){
								attributeButtons('show');
							}else{
								attributeButtons('hide');
							}
						}else{
							$('#speGen').hide();
							keyButtons('hide');
							attributeButtons('hide');
						}
					}
				}
				// if there are specialization/generalization figures in existence
				if(wave.getState().get('speGen') != null && wave.getState().get('speGen') != '0'){
					// if I have selected a spe/gen figure
					if(wave.getState().get('speGen').indexOf(selectedByMe.split("_")[0]) > -1){
						attributeButtons('hide'); // hide the attribute buttons
						keyButtons('hide'); // hide the key attributes
						speGenButtons('show'); // show the specific spe/gen buttons
						$('#speGen').hide(); // hide the button that inserts spe/gen figures

						speGenArr = wave.getState().get('speGen').split(",");
						for(var x = 0; x < speGenArr.length; x++){
							speGenArrParts = speGenArr[x].split("_");
							if(speGenArrParts.indexOf(selectedByMe.split("_")[0]) > -1){
								speGenPos = speGenArrParts.indexOf(selectedByMe.split("_")[0]);
								break;
							}
						}
						// the type of spe/gen is obtained
						// by adding one position to that
						// spe/gen's id position
						var speGenType = speGenArrParts[speGenPos+1];
						// depending of which type of spe/gen the selected
						// figure is, some menu buttons will be shown
						// or hidden
						if(speGenType == 'type:0'){
							$("#disjoint").show();
							$("#overlapping").show();
							$("#union").show();
							$("#removeSpeGen").hide();
						}else if(speGenType == 'type:1'){
							$("#disjoint").hide();
							$("#overlapping").show();
							$("#union").show();
							$("#removeSpeGen").show();
						}else if(speGenType == 'type:2'){
							$("#disjoint").show();
							$("#overlapping").hide();
							$("#union").show();
							$("#removeSpeGen").show();
						}else if(speGenType == 'type:3'){
							$("#disjoint").show();
							$("#overlapping").show();
							$("#union").hide();
							$("#removeSpeGen").show();
						}
					}
				}
			}else{ // if I have selected more than a signle object
				$('#speGen').hide();
				keyButtons('hide');
				attributeButtons('hide');
				speGenButtons('hide');
			}
		}
	}else{
		none_selected = 1;
	}

	if(none_selected == 1) {
		auxMenuHide();
	}

}


/*
 * Function that helps auxMenuControl hide the dynamic menu with the help
 * of JQuery.
 */
function auxMenuHide() {
	$('#auxMenuWrapper').fadeOut();
	$('#auxMenuSlideButton1').fadeOut();
	$('#auxMenuSlideButton2').fadeIn();
}



/*
   Object Insertion Functions
 */


/* 
 * Function used to paint an entity on the temporal canvas
 * indicating where the entity will be painted when a mouse
 * click is made. The temporal entity will have grey lines
 * to indicate that it's not final.
 */
function drawTempEntity() {
	posXIni	= posX - (entityWidth / 2);
	posYIni	= posY - (entityHeight / 2);
	checkTempBoundaries();
	if(option == '7'){ // strong entity
		context.clearRect(0, 0, canvas.width, canvas.height);
		context.strokeRect(
				posXIni,
				posYIni,
				entityWidth,
				entityHeight
				);
	}else if(option == '8'){ // weak entity
		// outer rectangle
		context.clearRect(0, 0, canvas.width, canvas.height);
		context.strokeRect(
				posXIni,
				posYIni,
				weakEntityWidth,
				weakEntityHeight
				);

		// inner rectangle
		context.strokeRect(
				posXIni+10,
				posYIni+10,
				weakEntityWidth-20,
				weakEntityHeight-20
				);
	}
}

/* 
 * Function used to paint an relation on the temporal canvas
 * indicating where the relation will be painted when a mouse
 * click is made. The temporal relation will have grey lines
 * to indicate that it's not final.
 */
function drawTempRelation() {
	posXIni	= posX - (relationWidth / 2) + 1;
	posYIni	= posY - (relationHeight / 2) + 1;
	checkTempBoundaries();
	if(option == '5'){
		context.clearRect(0, 0, canvas.width, canvas.height);
		context.beginPath();
		context.moveTo(posXIni, posY);
		context.lineTo(posX, posYIni);
		context.lineTo((posXIni + relationWidth), posY);
		context.lineTo(posX,(posYIni + relationHeight));
		context.closePath();
		context.stroke();
	}else if(option == '14'){
		context.clearRect(0, 0, canvas.width, canvas.height);

		//outer romboid
		context.beginPath();
		context.moveTo(posXIni, posY);
		context.lineTo(posX, posYIni);
		context.lineTo((posXIni + relationWidth), posY);
		context.lineTo(posX,(posYIni + relationHeight));
		context.closePath();
		context.stroke();

		//inner romboid
		context.beginPath();
		context.moveTo(posXIni+15, posY);
		context.lineTo(posX, posYIni+10);
		context.lineTo((posXIni + relationWidth-15), posY);
		context.lineTo(posX,(posYIni + relationHeight-10));
		context.closePath();
		context.stroke();
	}
}	

/* 
 * Function used to paint an attribute on the temporal canvas
 * indicating where the attribute will be painted when a mouse
 * click is made. The temporal attribute will have grey lines
 * to indicate that it's not final.
 */
function drawTempAttribute() {
	posXIni	= posX;
	posYIni	= posY;
	checkTempBoundaries();
	context.clearRect(0, 0, canvas.width, canvas.height);
	if(option == '6'){ // attribute's circule
		context.beginPath();
		context.arc(posXIni, posYIni, attributeRadius, 0, Math.PI * 2, false);
		context.closePath();
		context.stroke();
	}else if(option == '10'){ // multivalued attribute
		// outer circle
		context.beginPath();
		context.arc(posXIni, posYIni, attributeRadius, 0, Math.PI * 2, false);
		context.closePath();
		context.stroke();

		// inner circle
		context.beginPath();
		context.arc(posXIni, posYIni, attributeRadius-3, 0, Math.PI * 2, false);
		context.closePath();
		context.stroke();
	}else if(option == '11'){ // derived attribute
		var grain = Math.PI / attributeRadius;
		var startAngle = 0;
		var endAngle = startAngle + grain;
		while(endAngle < (Math.PI * 2)){
			context.beginPath();
			context.arc(posXIni, posYIni, attributeRadius, startAngle, endAngle, false);
			if(endAngle >= (Math.PI * 2)) context.closePath();
			context.stroke();
			startAngle = endAngle + grain;
			endAngle = startAngle + grain;
		}
	}
}

/*
 * Funtion used to paint a specialization/generalization figure on the temporal canvas,
 * indicating where the speGen figure will be drawn when the user
 * clicks on the canvas. The speGen figure will have grey lines
 * indicaring that it's not final.
 */
function drawTempSpeGen(){
	posXIni	= posX;
	posYIni	= posY;
	checkTempBoundaries();
	context.clearRect(0, 0, canvas.width, canvas.height);
	context.beginPath();
	context.arc(posXIni, posYIni, speGenRadius, 0, Math.PI * 2, false);
	context.closePath();
	context.stroke();
}

/*
 * The checkTempBoundaries function is called when a temporal
 * object is drawn in order to make it stay inside the canvas.
 * If the temporal object is within the canvas, the final
 * object will be inside too.
 */
function checkTempBoundaries() {
	if(option == '7' || option == '8'){ // checking for an entity
		if(posXIni < 1) {
			posXIni	= 1;
		}
		if(posYIni < 1) {
			posYIni	= 1;
		}
		if(posXIni > canvasWidth - entityWidth) {
			posXIni = canvasWidth - entityWidth;
		}
		if(posYIni > (canvasHeight - entityHeight)) {
			posYIni = canvasHeight - entityHeight;
		}
	}else if(option == '5' || option == '14'){ // checking for relations
		if(posXIni < 1) {
			posXIni = 1;
			posX = (relationWidth / 2) + 1;
		}
		if(posYIni < 1) {
			posYIni = 1;
			posY = (relationHeight / 2) + 1;
		}
		if(posXIni > canvasWidth - relationWidth){
			posXIni = canvasWidth - relationWidth - 1;
			posX = canvasWidth - (relationWidth / 2);
		}
		if(posYIni > canvasHeight - relationHeight){
			posYIni = canvasHeight - relationHeight - 1;
			posY = canvasHeight - (relationHeight / 2);
		}
	}else if(option == '6' || option == '10' || option == '11'){ // checking for attributes
		if(posXIni < Math.ceil(attributeRadius)){
			posXIni = 1;
			posX = Math.ceil(attributeRadius) + 1;
		}
		if(posYIni < Math.ceil(attributeRadius)){
			posYIni = 1;
			posY = Math.ceil(attributeRadius) + 1;
		}
		if(posXIni > canvasWidth - Math.ceil(attributeRadius)){
			posXIni = canvasWidth - Math.ceil(attributeRadius) - 1;
			posX = canvasWidth - (Math.ceil(attributeRadius) / 2);
		}
		if(posYIni > canvasHeight - Math.ceil(attributeRadius)){
			posYIni = canvasHeight - Math.ceil(attributeRadius) - 1;
			posY = canvasHeight - (Math.ceil(attributeRadius) / 2);
		}
	}else if(option == '16'){ // checking for spe/gen figure
		if(posXIni < Math.ceil(speGenRadius)){
			posXIni = 1;
			posX = Math.ceil(speGenRadius) + 1;
		}
		if(posYIni < Math.ceil(speGenRadius)){
			posYIni = 1;
			posY = Math.ceil(speGenRadius) + 1;
		}
		if(posXIni > canvasWidth - Math.ceil(speGenRadius)){
			posXIni = canvasWidth - Math.ceil(speGenRadius) - 1;
			posX = canvasWidth - (Math.ceil(speGenRadius) / 2);
		}
		if(posYIni > canvasHeight - Math.ceil(speGenRadius)){
			posYIni = canvasHeight - Math.ceil(speGenRadius) - 1;
			posY = canvasHeight - (Math.ceil(speGenRadius) / 2);
		}
	}
}
/*
 * The drawObject function is a general function to draw objects.
 * e.g: entities, relations or attributes.
 * It also updates the object's corresponding states.
 * Input: a variable called which is called which will have the
 * name of the to-be-drawn object, which corresponds to the name
 * of the object's state in Wave.
 * e.g:
 * 	'entity'
 * Beginning: The user wants to draw an object on the canvas, and so
 * the Gadget gets the current state in order to append the new object.
 * End: The function appends the new object to the object's state. Also
 * it updates the coordinates state, so the object can be drawn on the
 * canvas. It also updates the 'contributions' state to show who did
 * the last change, and what he drew.
 */
function drawObject(which) {
	if(wave && wave.getState() && canEdit && canDraw) {
		canDraw = 0;
		var delta	= {};
		delta[which]	= "";
		delta['contributions']	= "";
		delta['coordinates']	= "";

		if(wave.getState().get(which) &&
				wave.getState().get(which) != '0' &&
				wave.getState().get(which) != undefined) {

			delta[which] = "";
			delta[which] = wave.getState().get(which) + ",";
		}

		if(wave.getState().get('coordinates') &&
				wave.getState().get('coordinates') != '0' && 
				wave.getState().get('coordinates') != undefined) {

			delta['coordinates'] = "";
			delta['coordinates'] = wave.getState().get('coordinates') + ",";
		}

		var id = wave.getTime();
		var myColor = wave.getPrivateState().get('myColor');

		switch(option) {
			case '5':	// when drawing relations, their default participation
				// cards will have no vaule, this value is later
				// set in the drawLine() function ('ent:' and 'card:')
				delta['relation']	+= "id:" + id  + "_type:strong_owner:" + myColor + "_text:"; 
				delta['coordinates']	+= "id:" + id + "_type:relation" + 
					"_coord:" +  parseInt(posXIni) + "-"+ parseInt(posYIni);
				break;
			case '6':	// when drawing simple attributes.
				// This section of the code is used to draw attributes.
				// For this, a series of previous validations must be done.
				// First, there must be valid values inside of the 'selection'
				// state; this is done in case of a bug in Wave.
				// After this, the function checks whether there's only
				// one selected object or not by using the countMatches()
				// function (there must only be one).
				// The function attributeProcess() is called in order to
				// configure the to-be-sent delta according to the attribute's
				// type. Later, that delta is sent to the function called
				// compoundAttributes(), where it will be modified in order to
				// detect if an attribute has acquired a son, in other words, if
				// it has become a compound attribute. comoundAttributes() will
				// also modify the attribute's type if it has lost all of its sons,
				// in other words, when the attribute goes back to simple.
				// Finally, the delta is uploaded at the end of this switch.
				if(wave.getState().get('selected') && wave.getState().get('selected') != null && wave.getState().get('selected') != '0') {
					var selected	= wave.getState().get('selected');
					if(countMatches(selected,"who:" + myColor) == 1) {
						var coordinates	= delta['coordinates'].split(",");
						var atrDeltaArray = [];
						var switchToComp = 0;
						atrDeltaArray = attributeProcess(selected, coordinates, id, myColor);
						if(atrDeltaArray != undefined && atrDeltaArray != null){
							delta['attribute'] += atrDeltaArray['attribute'];
							delta['coordinates'] += atrDeltaArray['coordinates'];
							delta['line'] = "";
							delta['line'] = atrDeltaArray['line'];
							var lineId = atrDeltaArray['lineId'];
							switchToComp = atrDeltaArray['switchToComp'];
						}
						if(switchToComp){
							var compAttrDelta = [];
							compAttrDelta['attribute'] = compoundAttributes(selected, delta['attribute'], 1, 0, null, null);
							if(compAttrDelta['attribute'] != ""){
								delta['attribute'] = compAttrDelta['attribute'];
							}
						}
					} else {
						return;
					}
				}
				// The temporal canvas is cleaned (cleared)
				context.clearRect(0, 0, canvas.width, canvas.height);
				break;	// end of the attribute's case
			case '7':	// when drawing entities			
				delta['entity']	+= "id:" + id + "_type:strong_owner:" + myColor + "_text:";
				delta['coordinates']	+= "id:" + id + "_type:entity" + 
					"_coord:"+  parseInt(posXIni) + "-"+ parseInt(posYIni);
				break;	
			case '8':	// when drawing weak entities			
				delta['entity']	+= "id:" + id + "_type:weak_owner:" + myColor + "_text:";
				delta['coordinates']	+= "id:" + id + "_type:entity" + 
					"_coord:"+  parseInt(posXIni) + "-"+ parseInt(posYIni);
				break;		
			case '10':	// when drawing multivalued attributes.
				// This section of the code is used to draw multivalued attributes.
				// For this, a series of previous validations must be done.
				// First, there must be valid values inside of the 'selection'
				// state; this is done in case of a bug in Wave.
				// After this, the function checks whether there's only
				// one selected object or not (there must only be one).
				// Then, the desired object's id is gotten from within the
				// array that contains the selected objects. The id mentioned
				// previously will be the attribute's owner.
				if(wave.getState().get('selected') && wave.getState().get('selected') != null && wave.getState().get('selected') != '0') {
					var selected = wave.getState().get('selected');
					if(countMatches(selected,"who:" + myColor) == 1) {
						var coordinates	= delta['coordinates'].split(",");
						var atrDeltaArray = [];
						var switchToComp = 0;
						atrDeltaArray = attributeProcess(selected, coordinates, id, myColor);
						if(atrDeltaArray != undefined && atrDeltaArray != null){
							delta['attribute'] += atrDeltaArray['attribute'];
							delta['coordinates'] += atrDeltaArray['coordinates'];
							delta['line'] = "";
							delta['line'] = atrDeltaArray['line'];
							var lineId = atrDeltaArray['lineId'];
						}
					} else {
						return;
					}
				}
				// The temporal canvas is cleaned (cleared)
				context.clearRect(0, 0, canvas.width, canvas.height);
				break;	// end of the attribute's case
			case '11':	// when drawing derived attributes.
				// This section of the code is used to draw multivalued attributes.
				// For this, a series of previous validations must be done.
				// First, there must be valid values inside of the 'selection'
				// state; this is done in case of a bug in Wave.
				// After this, the function checks whether there's only
				// one selected object or not (there must only be one).
				// Then, the desired object's id is gotten from within the
				// array that contains the selected objects. The id mentioned
				// previously will be the attribute's owner.
				if(wave.getState().get('selected') && wave.getState().get('selected') != null && wave.getState().get('selected') != '0') {
					var selected	= wave.getState().get('selected');
					if(countMatches(selected,"who:" + myColor) == 1) {
						var coordinates	= delta['coordinates'].split(",");
						var atrDeltaArray = [];
						var switchToComp = 0;
						atrDeltaArray = attributeProcess(selected, coordinates, id, myColor);
						if(atrDeltaArray != undefined && atrDeltaArray != null){
							delta['attribute'] += atrDeltaArray['attribute'];
							delta['coordinates'] += atrDeltaArray['coordinates'];
							delta['line'] = "";
							delta['line'] = atrDeltaArray['line'];
							var lineId = atrDeltaArray['lineId'];
						}
					} else {
						return;
					}
				}
				// The temporal canvas is cleaned (cleared)
				context.clearRect(0, 0, canvas.width, canvas.height);
				break;	// end of the attribute's case
			case '14':	// when drawing relations, their default participation
				// cards will have no vaule, this value is later
				// set in the drawLine() function ('ent:' and 'card:')
				delta['relation']	+= "id:" + id  + "_type:weak_owner:" + myColor + "_text:"; 
				delta['coordinates']	+= "id:" + id + "_type:relation" + 
					"_coord:" +  parseInt(posXIni) + "-"+ parseInt(posYIni);
				break;
			case '16':	// when drawing a specialization/generalization figure
				var coordinates = wave.getState().get('coordinates').split(',');
				var selected = wave.getState().get('selected');
				var selectedArray = selected.split(',');
				for(x in selectedArray){
					if(selectedArray[x].indexOf("who:" + myColor) != -1){
						var selectedParts = selectedArray[x].split("_");
						for(y in coordinates) {
							if(coordinates[y].indexOf(selectedParts[0]) != -1){
								var coordinatesParts	= coordinates[y].split("_");
								var endCoordinates	= (coordinatesParts[2].split(":"))[1].split("-");
								var fatherId = selectedParts[0].split(":")[1];
								var coordinatesCenterX	= entityWidth / 2;
								var coordinatesCenterY	= entityHeight / 2;
								break;
							}
						}
						break;
					}
				}
				delta['line'] = "";
				delta['speGen'] += "id:" + id + "_type:0_owner:" + myColor + "_father:" + fatherId;
				delta['coordinates'] += "id:" + id + "_type:speGen" + 
					"_coord:" +  parseInt(posXIni) + "-"+ parseInt(posYIni);
				canDraw = 1;	 // viariable that tells the gadget that the user is able to draw.
				// it is declared as 1 here because it's normally switched to
				// true in the function insertName, which option 16 doesn't use.
				var lineId = (parseInt(wave.getTime()) + 1);

				if(wave.getState().get('line') && wave.getState().get('line') != '0' && wave.getState().get('line') != undefined) {
					delta['line'] = wave.getState().get('line') + ",";
				}

				delta['line'] += "id:" + lineId + "_coordI:" + parseInt(posXIni) + "-" + 
					parseInt(posYIni) + "_coordF:" + (parseInt(endCoordinates[0]) + coordinatesCenterX) + "-" + 
					(parseInt(endCoordinates[1]) + coordinatesCenterY) + "_speGen:" + id + "_ent:" + fatherId;
				break;
			default:
				break;
				//end of switch
		}

		/** Begin of section that creates the 'cotributions' state when something is added **/
		var addedObjects = "";
		if(lineId == undefined){ // If a signe object was added (without a line)...
			addedObjects = id; // asign the id of the added object to addedObjects.
		}else{ // If an object that requires an automatic line was added (attributes)...
			addedObjects = id + "-" + lineId; 	// asign the id of that added object plus
			// the line's id to addedObjects.
		}
		delta['contributions']	+= "id:" + addedObjects + "_contributor:" + 
			myColor + "_action:AddObject";
		/** End of section that creates the 'cotributions' state when something is added **/

		selectThis	= wave.getState().get('selected');
		if(selectThis	== null || selectThis	== undefined) {
			selectThis	= '0';
		}
		if(selectThis	== '0') {
			delta['selected'] = "id:" + id + "_who:" + myColor;
		} else {
			delta['selected'] = selectThis + ",id:" + id + "_who:" + myColor;
		}
		wave.getState().submitDelta(delta);
		if(option != '16'){ // specialization/generalization figures dont need a name
			insertName(which, id, posXIni, posYIni, null);
		}
	}
	if(hasNewName){				// This if controls if the instructions' HTML
		generalControl('1');	// div changes to 'Ready' or stays with 'Insert name...'
	}
}	

/* 
 * Function in charge of changing the type of an attribute whether it
 * becomes compound or simple. When changing from simple to compound
 * it takes as input which attribute has been selected and modifies 
 * the to-be-uploaded delta in order to modify the selected attribute's 
 * type when switching to comp. When changing from compound to simple 
 * it takes the parent attribute which got  its sons erased and
 * returns a to-be-uploaded delta with the parent's new type.
 */
function compoundAttributes(selected, attrDelta, switchToComp, switchToNormal, parentAttr, attributes){
	if(switchToComp){
		if(attrDelta != null && attrDelta != '0'){
			// contains the id of the selected attribute
			var idAttribute = selected.split("_")[0];
			// contains the attributes' delta received as input,
			// this delta has already been processed in the
			// function callled attributeProcess, but it won't be
			// submitted until this function finishes.
			var attribute = attrDelta;
			var separatedAttributes = attribute.split(",");
			// boolean to indicate whether the attributes' delta
			// must be modified.
			var hasChanged = 0;
			var compAttrDelta = [];
			compAttrDelta['attribute'] = "";

			for(var x = 0; x < separatedAttributes.length; x++){
				var attributeParts = separatedAttributes[x].split("_");
				var idPos = attributeParts.indexOf(idAttribute);
				if(idPos != -1){
					var typePos = idPos + 2;
					if(attributeParts[typePos] == 'type:simple'){
						attributeParts[typePos] = 'type:comp';
						hasChanged = 1;
						break;
					}
				}
			}

			if(hasChanged){
				var changedAttribute = attributeParts.join("_");
				separatedAttributes[x] = changedAttribute;
				compAttrDelta['attribute'] = separatedAttributes.join(",");
			}
			return compAttrDelta['attribute'];
		}
	}else if(switchToNormal){
		var attributes = attributes + "";
		var hasChanged = 0;
		var attributesArr = attributes.split(",");
		for(var x = 0; x < attributesArr.length; x++) {
			attributesArrParts = attributesArr[x].split("_");
			if(attributesArrParts[0] == parentAttr) {
				if(attributesArrParts[2] == 'type:comp') {
					attributesArrParts[2] = 'type:simple';
					hasChanged = 1;
					break;
				}
			}
		}

		if(hasChanged) {
			var changedAttribute = attributesArrParts.join("_");
			attributesArr[x] = changedAttribute;
			attributes = attributesArr.join(",");
		}
		return attributes;
	}
}


/*
 * Function in charge of switching between an attribute's types
 * as the user clicks on the action buttons. Changes can be done
 * from simple or multivalued into key or partial key
 * and viceversa.
 * key:0 means that the attribute is not a key
 * NOTE: derived attribtues cannot be keys, therefore they do
 * not have this field.
 * key:1 means key
 * key:2 means partial key
 * After switching the key type, the attribute delta is reuploaded
 * with the changes.
 */
function switchAttrType(action){
	var switchMe = "";
	var selectedByMe = "";
	var myColor = wave.getPrivateState().get('myColor');
	var selected = wave.getState().get('selected');
	var selectedArr = selected.split(',');

	// get the figure whom I whish to switch types
	for(var i = 0; i < selectedArr.length; i++){
		var selectedArrParts = selectedArr[i].split('_');
		if(selectedArrParts[1] == 'who:' + myColor){
			selectedByMe = selectedArr[i];
		}
	}
	var attribute = wave.getState().get('attribute');
	// gets the id of the selected (by me) object
	var target = selectedByMe.split('_')[0].split(':')[1];
	var attrArr = attribute.split(',');
	for(var x = 0; x < attrArr.length; x++){
		// gets the id of that item in the attributes' array
		var attrArrId = attrArr[x].split('_')[0].split(':')[1];
		if(target == attrArrId){
			switchMe = attrArr[x];
			break;
		}
	}

	var switchMeArr = switchMe.split('_');

	if(action == '12') { // switch to key
		switchMeArr.splice(3,1,"key:1"); // change the key type
	}else if(action == '13') { // switch to partial key
		switchMeArr.splice(3,1,"key:2"); // change the key type
	}else if(action == '15') { // remove key
		switchMeArr.splice(3,1,"key:0"); // change the key type
	}

	switchMe = switchMeArr.join('_');
	attrArr.splice(x,1,switchMe);
	attribute = attrArr.join(',');
	var delta = {};
	delta['attribute'] = attribute;
	wave.getState().submitDelta(delta);
}

/*
 *Function in charge of switching between a spe/gen figure's types
 * as the user clicks on the action buttons. Changes can be done
 * between disjoint, overlapping, union or none types.
 * type:0 means that the figure has no type
 * type:1 means disjoint
 * type:2 means overlapping
 * type:3 means union
 * After switching the figure's type, the spe/gen delta is reuploaded
 * with the changes.
 */
function switchSpeGenType(action){	
	var switchMe = "";
	var selectedByMe = "";
	var myColor = wave.getPrivateState().get('myColor');
	var selected = wave.getState().get('selected');
	var selectedArr = selected.split(',');

	// get the attribute whom I whish to switch keys
	for(var i = 0; i < selectedArr.length; i++){
		var selectedArrParts = selectedArr[i].split('_');
		if(selectedArrParts[1] == 'who:' + myColor){
			selectedByMe = selectedArr[i];
		}
	}
	var speGen = wave.getState().get('speGen');
	// gets the id of the selected (by me) object
	var target = selectedByMe.split('_')[0].split(':')[1];
	var speGenArr = speGen.split(',');
	for(var x = 0; x < speGenArr.length; x++){
		// gets the id of that item in the attributes' array
		var speGenArrId = speGenArr[x].split('_')[0].split(':')[1];
		if(target == speGenArrId){
			switchMe = speGenArr[x];
			break;
		}
	}
	var switchMeArr = switchMe.split('_');

	if(action == '17') { // switch to disjoint
		switchMeArr.splice(1,1,"type:1"); // change the spe/gen type
	}else if(action == '18') { // switch to overlapping
		switchMeArr.splice(1,1,"type:2"); // change the spe/gen type
	}else if(action == '19') { // switch to union
		switchMeArr.splice(1,1,"type:3"); // change the spe/gen type
	}else if(action == '20') { // remove spe/gen type
		switchMeArr.splice(1,1,"type:0"); // change the spe/gen type
	}
	switchMe = switchMeArr.join('_');
	speGenArr.splice(x,1,switchMe);
	speGen = speGenArr.join(',');
	var delta = {};
	delta['speGen'] = speGen;
	wave.getState().submitDelta(delta);
}

function attributeProcess(selected, coordinates, id, myColor){
	var selectedArray = selected.split(",");
	for(x in selectedArray) {
		if(selectedArray[x].indexOf("who:" + myColor) != -1) {
			var selectedParts = selectedArray[x].split("_");
			for(y in coordinates) {
				if(coordinates[y].indexOf(selectedParts[0]) != -1) {
					var coordinatesParts	= coordinates[y].split("_");
					var endCoordinates	= (coordinatesParts[2].split(":"))[1].split("-");
					var whatTypeOfObject	= ""; // To which object the attribute belongs.
					var selectedObjectId	= selectedParts[0].split(":");
					var coordinatesCenterX	= 0;
					var coordinatesCenterY	= 0;
					// array that will be returned with all the deltas created here
					// and a boolean that defines if an attribute will be compound
					var atrDeltaArray = [];
					atrDeltaArray['attribute'] = "";
					atrDeltaArray['coordinates'] = "";
					atrDeltaArray['line'] = "";
					atrDeltaArray['switchToComp'] = 0;

					// checks what type of object will acquire the attribute
					if(coordinatesParts[1]	== "type:entity") {
						whatTypeOfObject	= "ent";
						coordinatesCenterX	= entityWidth / 2;
						coordinatesCenterY	= entityHeight / 2;
					} else if(coordinatesParts[1]	== "type:relation") {
						whatTypeOfObject	= "rel";
						coordinatesCenterX	= relationWidth / 2;
						coordinatesCenterY	= relationHeight / 2;
					} else if(coordinatesParts[1]	== "type:attribute") {
						whatTypeOfObject	= "attr";
						coordinatesCenterX	= attributeRadius / 2;
						coordinatesCenterY	= attributeRadius / 2;
						atrDeltaArray['switchToComp'] = 1;
					}

					if(option == '6'){
						atrDeltaArray['attribute'] += "id:" + id + "_" + whatTypeOfObject +
							":" + selectedObjectId[1] +  "_type:simple_key:0_owner:" + myColor + "_text: ";
					}else if(option == '10'){
						atrDeltaArray['attribute'] += "id:" + id + "_" + whatTypeOfObject + 
							":" + selectedObjectId[1] + "_type:multi_key:0_owner:" + myColor + "_text: ";
					}else if(option == '11'){
						atrDeltaArray['attribute'] += "id:" + id + "_" + whatTypeOfObject + 
							":" + selectedObjectId[1] + "_type:derived_owner:" + myColor +"_text: ";
					}

					atrDeltaArray['coordinates'] += "id:" + id + "_type:attribute_coord:" + parseInt(posXIni) + "-" + parseInt(posYIni);

					if(wave.getState().get('line') && wave.getState().get('line') != '0' && wave.getState().get('line') != undefined) {
						atrDeltaArray['line'] = wave.getState().get('line') + ",";
					}

					// variable needed for the addObject action,
					// it gives us the id of the line that connects
					// the attribute with its respective object.
					var lineId = (parseInt(wave.getTime()) + 1);
					atrDeltaArray['lineId'] = lineId;

					atrDeltaArray['line'] += "id:" + lineId + "_coordI:" + parseInt(posXIni) + "-" + 
						parseInt(posYIni) + "_coordF:" + (parseInt(endCoordinates[0]) + coordinatesCenterX) + "-" + 
						(parseInt(endCoordinates[1]) + coordinatesCenterY) + "_attr:" + id + "_" + whatTypeOfObject + 
						":" + selectedObjectId[1];

					return atrDeltaArray;

					break;
				}
			}
			break;
		}
	}
}

/*
 * Function to insert the name of an object.
 * Input: 
 * 	which - what type of object.
 * 	id - the id of the object.
 * 	posX - the position of the object on the X axis
 * 		inside the canvas.
 * 	posY - the position of the object on the Y axis
 * 		inside the canvas.
 * 	name - if the object already has a name, the
 * 	previous name will go here. Else, the value
 * 	of this variable will be null.
 * Beginning: if the function is called when the
 * object was first created, the object has no name.
 * End: the object is assigned a name that will be
 * drawn on the canvas (this is not intended to be
 * used as an Id).
 * TODO - Since this function is intented to be used
 * whether an object already has a name or not, another
 * function must be done to find the id, posX, posY,
 * and the previous name of the variable which is
 * to be renamed.
 */
function insertName(which, id, posX, posY, name){
	indicationsDiv.innerHTML = "<label>Type the object's name and press ENTER</label>";
	$('.tapa-botones').fadeIn();
	$('#auxMenuDiv').fadeOut();

	// textbox position in Y and X
	var posYBox = '' + parseInt(posYIni * zoom) + 'px';
	var posXBox = '' + parseInt(posXIni * zoom) + 'px';
	// textbox width and height
	var textBoxW = '' + parseInt(75 * zoom) + 'px';
	var textBoxH = '' + parseInt(25 * zoom) + 'px';
	// the size of the textbox's text
	var textBoxTS = '' + parseInt(15 * zoom) + 'px'; // the deafault font size is 15px
	// operations needed in order to center the textbox
	if(which == "entity"){
		// the entity's middle points
		entityMPX = parseInt(posXIni * zoom) + (zoom * entityWidth/2);
		entityMPY = parseInt(posYIni * zoom) + (zoom * entityHeight/2);
		// the textbox's centered points
		textBoxCPX = entityMPX - (parseInt(73 * zoom)/2); // the textbox's width is 75px
		textBoxCPY = entityMPY - (parseInt(25 * zoom)/2); // the textbox's height is 25px
	}else if(which == "relation"){
		// the relation's middle points
		relationMPX = parseInt(posXIni * zoom) + (zoom * relationWidth/2);
		relationMPY = parseInt(posYIni * zoom) + (zoom * relationHeight/2);
		// the textbox's centered points
		textBoxCPX = relationMPX - (parseInt(74 * zoom)/2); // the textbox's width is 75px
		textBoxCPY = relationMPY - (parseInt(25 * zoom)/2); // the textbox's height is 25px
	}else if(which == "attribute"){
		// the textbox's centered points
		textBoxCPX = parseInt(posX * zoom);
		textBoxCPY = parseInt(posY * zoom);
	}

	// creation of the textbox where the name will be inserted
	var textBox = document.createElement("input");
	textBox.setAttribute("type", "text");
	textBox.setAttribute("value", "");
	textBox.setAttribute("name", "insertText");
	textBox.setAttribute("maxlength", "36");
	textBox.setAttribute("style", "position:relative;left:"+textBoxCPX+";top:"+textBoxCPY+";width:"+textBoxW+";height:"+textBoxH+";font-size:"+textBoxTS+"");
	textBoxId = "t-" + wave.getTime();
	textBox.id=textBoxId;
	jQueryTextBoxId	= "#" + textBoxId;

	var contentDiv	= document.getElementById('contentDiv');
	contentDiv.appendChild(textBox);

	var tempPageX	= 0;
	var tempPageY	= 0;

	$(jQueryTextBoxId).show();
	$('#imageTemp').hide();
	$(jQueryTextBoxId).focus();
	if(name != null && name != undefined) {
		$(jQueryTextBoxId).val(name);
	}

	$(jQueryTextBoxId).keyup(function(event) {

			var keyC	= (event.keyCode ? event.keyCode : event.which);
			$(this).val($(this).val().replace("_",""));
			$(this).val($(this).val().replace(",",""));
			$(this).val($(this).val().replace(":",""));
			$(this).val($(this).val().replace("'",""));
			if($(this).val().length > 36) {
			$(this).val($(this).val().substring(0,36));
			}

			if(keyC	== '13') {
			hasNewName = 1;
			canDraw = 1;
			var state = wave.getState().get(which);
			// 'which' stands for the object that will be named
			// and 'state' stands for that object type's state.
			var stateArray	= state.split(",");

			// If no name is written for the newly created object a default
			// name is given to it.
			if( $(this).val()	== "") {
				var tradWhich	= "";
				if(which	== 'relation') {
					tradWhich	= 'Relation';
				} else if(which	== 'entity' && option == '7'){ //normal entity
					tradWhich	= 'Entity';
				} else if(which	== 'entity' && option == '8'){ //weak entity
					tradWhich	= 'WeakEntity'; 
				}else if(which	== 'attribute') {
					tradWhich	= 'Attribute';
				}

				// This is the numbering process for the object that will 
				// receive a default name. The default name will be something
				// like unnamedEntityX where x is the number of unnamed
				// entities.
				var i	= 1;
				var name = tradWhich + i;
				while(1) {
					if(state.indexOf(name)	== -1) {
						break;
					} else {
						i++;
						name = tradWhich + i;
					}
				}
				for(x in stateArray) {
					if(stateArray[x].indexOf(id) != -1) {
						stateArray[x]	+= name;
						break;
					}
				}

				state = stateArray.join(",");
				var tempDelta = {};
				tempDelta[which] = state;
				wave.getState().submitDelta(tempDelta);
			} else {
				for(x in stateArray) {
					// stateArray[x] contains all the information from the
					// elements that belong to that object type
					if(stateArray[x].indexOf(id) != -1) {
						$(this).val($(this).val().replace("_",""));
						$(this).val($(this).val().replace(",",""));
						$(this).val($(this).val().replace(":",""));
						$(this).val($(this).val().replace("'",""));
						// The next instruction inserts the new name in to the 
						// delta that will be submitted.
						stateArray[x] += $(this).val();
						break;
					}
				}

				state = stateArray.join(",");
				var tempDelta = {};
				tempDelta[which] = state;
				wave.getState().submitDelta(tempDelta);
			}

			$('#imageTemp').show();
			$(jQueryTextBoxId).blur();
			$(jQueryTextBoxId).hide();
			$(jQueryTextBoxId).val('');
			$(jQueryTextBoxId).unbind(event);
			context.clearRect(0, 0, canvas.width, canvas.height);

			eraseTextBox(textBox);
			generalControl('1');
			$('.tapa-botones').fadeOut();
			$('#auxMenuDiv').fadeIn();
			}
			hasNewName = 0;	// The boolean flag is reset so that new
			// unnamed objects get default-named correctly
	});
}

function insertParticipation(participation, idCardlessRel){
	indicationsDiv.innerHTML = "<label>Type the participation and press ENTER</label>";
	$('.tapa-botones').fadeIn();
	$('#auxMenuDiv').fadeOut();
	var textBox	= document.createElement("input");

	// Textbox's attributes
	textBox.setAttribute("size", "1");
	textBox.setAttribute("type", "text");
	textBox.setAttribute("value", "");
	textBox.setAttribute("name", "insertPart");
	textBoxId	= "t-" + wave.getTime();

	textBox.id=textBoxId;

	jQueryTextBoxId	= "#" + textBoxId;

	var contentDiv	= document.getElementById('contentDiv');
	contentDiv.appendChild(textBox);

	var tempPageX	= 0;
	var tempPageY	= 0;

	var posYBox	= '' + parseInt(posYIni * zoom) + 'px';
	var posXBox	= '' + parseInt(posXIni * zoom) + 'px';

	$(jQueryTextBoxId).show();
	$(jQueryTextBoxId).attr('maxlength','1');
	$(jQueryTextBoxId).css({
			'position': 'relative',
			'left': posXBox,
			'top': posYBox
			});
	$('#imageTemp').hide();
	$(jQueryTextBoxId).focus();

	// If no 'participation' parameter is received
	if(participation != null && participation != undefined) {
		$(jQueryTextBoxId).val(participation);
	}else{
		$(jQueryTextBoxId).val("");
	}

	$(jQueryTextBoxId).keyup(function(event) {

			var keyC	= (event.keyCode ? event.keyCode : event.which);
			$(this).val($(this).val().replace("_",""));
			$(this).val($(this).val().replace(",",""));
			$(this).val($(this).val().replace(":",""));
			$(this).val($(this).val().replace("'",""));
			if($(this).val().length > 1) {
			$(this).val($(this).val().substring(0,1));
			}

			if(keyC	== '13') {
			hasNewParticipation = 1;
			participation = $(this).val();
			$('#imageTemp').show();
			$(jQueryTextBoxId).blur();
			$(jQueryTextBoxId).hide();
			$(jQueryTextBoxId).val('');
			$(jQueryTextBoxId).unbind(event);
			context.clearRect(0, 0, canvas.width, canvas.height);

			eraseTextBox(textBox);
			generalControl('1');
			$('.tapa-botones').fadeOut();
			$('#auxMenuDiv').fadeIn();
			var relation_delta = wave.getState().get('relation');
			// relation_delta sample:
			// id:1295234100372_ent:1295234098208_card:null_text:Relation1
			// The null value is always inserted in drawLine and replaced here.
			var indexOfCard = relation_delta.indexOf("_card:null_");
			var delta = {};
			delta['relation'] = relation_delta.substring(0,indexOfCard)
				+ "_card:" + participation + "_"
				+ relation_delta.substring(indexOfCard+11);
			wave.getState().submitDelta(delta);
			}
	});
}

/*
 * Function to eliminate the textbox that's dynamically
 * created when a name insertion is done.
 */
function eraseTextBox(textBox){
	var contentDiv	= document.getElementById('contentDiv');
	contentDiv.removeChild(textBox);
}

/*
   Selection Function
/*

/*
 * the startSelectionBox function is used to initialize the
 * drawing of a selection box.
 * It stores the position where the selection box was
 * initialized and stores the user's color because that's
 * the color the selection box will acquire.
 * The RGB to RGBA functions are used to give the selection box
 * a transparency effect.
 */
function startSelectionBox() {

	var colorSelection	=  colorsHex[wave.getPrivateState().get('myColor')];
	posXIni	= posX;
	posYIni	= posY;
	drawingSelectionBox	= true;

	context.clearRect(0, 0, canvas.width, canvas.height);
	context.fillStyle	= 'rgba(' + HexToR(colorSelection) + ',' + 
			HexToG(colorSelection) + ',' + HexToB(colorSelection) + ',' + '0.4)';
	context.strokeStyle	= colorSelection;

}

/*
 * The stopSelectionBox function is used to stop the drawing
 * of the selection box. This is used to re-initialize the
 * Selection tool's helper variables.
 */
function stopSelectionBox()	{

	drawingSelectionBox	= false;
	context.strokeStyle	= 'grey';
	context.clearRect(0, 0, canvas.width, canvas.height);

}

/*
 * The drawSelectionBox function is used to paint the
 * selection box. The temporal canvas is cleaned and then
 * the selection box is drawn. This is done again and again,
 * until the mouse button is released, to give a dynamic
 * effect.
 */
function drawSelectionBox() {

	context.clearRect(0, 0, canvas.width, canvas.height);

	var colorSelection	=  colorsHex[wave.getPrivateState().get('myColor')];
	context.fillStyle	= 'rgba(' + HexToR(colorSelection) + ',' + 
			HexToG(colorSelection) + ',' + HexToB(colorSelection) + ',' + '0.4)';
	context.strokeStyle	= colorSelection;

	context.fillRect(posXIni, posYIni, posX - posXIni, posY - posYIni);
	context.strokeRect(posXIni, posYIni, posX - posXIni, posY - posYIni);


}

/*
 * Function that selects an object individually.
 * Beginning: the user want's to select an object,
 * and so the Gadget check where the user clicked
 * and looks for an object that surround's the mouse's
 * coordinates.
 * End: the 'selected' state is updated. The object
 * that was selected is appended to the end of the
 * state, if no object was selected nothing happens.
 * If nobody has selected an object a '0' is used.
 */
function selectIndividual() {

	context.clearRect(0, 0, canvas.width, canvas.height);
	if(wave && wave.getState() && canEdit) {
		var delta	= {};
		var selectedAnything	= 0;
		delta['selected']	= "";

		// First we check if there are other objects selected
		if(wave.getState().get('selected') &&
				wave.getState().get('selected') != '0' &&
				wave.getState().get('selected') != undefined) {

			delta['selected']	= wave.getState().get('selected');
			var previous	= delta['selected'].indexOf("who:" +
					wave.getPrivateState().get('myColor'));
			var map1	= delta['selected'].split(",");

			if(previous != -1) {
				var temp	= "";

				for(x in map1) {
					if(map1[x].indexOf("who:" + wave.getPrivateState().get('myColor'))	== -1) {
						temp	+= map1[x] + ",";
					}
				}

				delta['selected']	= temp;
			} else {
				delta['selected']	+= ",";
			}


		}

		if(wave.getState().get('coordinates') &&
				wave.getState().get('coordinates') != '0' &&
				wave.getState().get('coordinates') != undefined) {

			var coordinates	= wave.getState().get('coordinates');
			var map2	= coordinates.split(",");

			// Then we look for the object that was selected.
			// This is done by checking the coordinates of the
			// objects in the 'coordinates' state.
			for(var index	=  map2.length -1; index >= 0;  index--) {
				var map3	= map2[index].split("_");
				var map4	= map3[2].split(":");
				var coord	= map4[1].split("-");

				if(map3[1] == "type:entity"){
					if(posX >= coord[0] && posY >= coord[1] &&
							posX <= (parseInt(coord[0]) + entityWidth) &&
							posY <= (parseInt(coord[1]) + entityHeight)) {

						if(delta['selected'].indexOf(map3[0])	== -1) {
							delta['selected'] += map3[0] + "_";
							selectedAnything = 1;
							break;
						}
					}
				}else if(map3[1] == "type:relation"){
					if(checkInsideRelation(posX,posY,coord[0],coord[1])) {

						if(delta['selected'].indexOf(map3[0]) == -1) {
							delta['selected'] += map3[0] + "_";
							selectedAnything = 1;
							break;
						}
					}
				}else if(map3[1] == "type:attribute"){
					var a = Math.pow(parseInt(posX) - parseInt(coord[0]), 2);
					var b = Math.pow(parseInt(posY) - parseInt(coord[1]), 2);
					var c = Math.sqrt(a + b);

					if(c <= attributeRadius) {
						if(delta['selected'].indexOf(map3[0]) == -1) {
							delta['selected'] += map3[0] + "_";
							selectedAnything = 1;
							break;
						}
					}
				}else if(map3[1] == "type:speGen"){
					var a = Math.pow(parseInt(posX) - parseInt(coord[0]), 2);
					var b = Math.pow(parseInt(posY) - parseInt(coord[1]), 2);
					var c = Math.sqrt(a + b);

					if(c <= speGenRadius) {
						if(delta['selected'].indexOf(map3[0]) == -1) {
							delta['selected'] += map3[0] + "_";
							selectedAnything = 1;
							break;
						}
					}
				}
			}

			// If something was selected, the new selection is added
			// to the 'selected' state. If nothing was selected, and
			// the 'selected' state had other users' selections, the
			// state is updated without this user's previous selections.
			// If there is nothing left in the 'selected' state, a '0'
			// is used.
			if(selectedAnything	== 1) {
				delta['selected']	+= "who:" + wave.getPrivateState().get('myColor');
				wave.getState().submitDelta(delta);
			} else {
				if(delta['selected'] != "") {
					delta['selected']	= delta['selected'].substring(
							0,delta['selected'].length-1);
					wave.getState().submitDelta(delta);
				} else {
					delta['selected']	= '0';
					wave.getState().submitDelta(delta);
				}
			}

		} else {
			delta['selected']	= '0';
			wave.getState().submitDelta(delta);
		}
	}

}

/*
 * The selectMultiple function selects multiple objects
 * in the canvas, this is done by checking the boundaries
 * of the selection box that was previously made.
 */
function selectMultiple() {

	if(wave && wave.getState() && canEdit) {
		var delta	= {};
		var selectedAnything	= 0;

		var posXIniBox	= 0;
		var posYIniBox	= 0;
		var posXBox	= 0;
		var posYBox	= 0;

		if(posXIni < posX) {
			posXIniBox	= posXIni;
			posXBox	= posX;
		} else{
			posXIniBox	= posX;
			posXBox	= posXIni;
		}

		if(posYIni < posY) {
			posYIniBox	= posYIni;
			posYBox	= posY;
		} else{
			posYIniBox	= posY;
			posYBox	= posYIni;
		}

		delta['selected']	= "";

		// First we check if there are other objects selected
		if(wave.getState().get('selected') &&
				wave.getState().get('selected') != '0' &&
				wave.getState().get('selected') != undefined) {

			delta['selected']	= wave.getState().get('selected');
			var previous	= delta['selected'].indexOf("who:" +
					wave.getPrivateState().get('myColor'));
			var map1	= delta['selected'].split(",");

			if(previous != -1) {
				var temp	= "";

				for(x in map1) {
					if(map1[x].indexOf("who:" + wave.getPrivateState().get('myColor'))	== -1) {
						temp	+= map1[x] + ",";
					}
				}

				delta['selected']	= temp;
			} else {
				delta['selected']	+= ",";
			}


		}

		if(wave.getState().get('coordinates') &&
				wave.getState().get('coordinates') != '0' &&
				wave.getState().get('coordinates') != undefined) {

			var coordinates	= wave.getState().get('coordinates');
			var map2	= coordinates.split(",");

			// Then we look for the object or objects that where selected.
			// This is done by checking the coordinates of the
			// objects in the 'coordinates' state.
			for(var index	=  map2.length -1; index >= 0;  index--) {
				var map3	= map2[index].split("_");
				var map4	= map3[2].split(":");
				var coord	= map4[1].split("-");

				if(map3[1]	== "type:entity") {
					if(posXIniBox <= coord[0] && posYIniBox <= coord[1] &&
							posXBox >= (parseInt(coord[0]) + entityWidth) &&
							posYBox >= (parseInt(coord[1]) + entityHeight)) {

						if(delta['selected'].indexOf(map3[0])	== -1) {
							if(selectedAnything	== 1) {
								delta['selected']	+= "," + map3[0] + 
									"_who:" + 
									wave.getPrivateState().get('myColor');
							} else {
								delta['selected']	+= map3[0] + 
									"_who:" + 
									wave.getPrivateState().get('myColor');
							}
							wave.getState().submitDelta(delta);
							selectedAnything	= 1;
						}
					}
				}else if(map3[1]	== "type:relation") {
					if(posXIniBox <= parseInt(coord[0]) && 
							posYIniBox <= parseInt(coord[1]) &&
							posXBox >= (parseInt(coord[0]) + relationWidth) &&
							posYBox >= (parseInt(coord[1]) + relationHeight)) {


						if(delta['selected'].indexOf(map3[0])	== -1) {
							if(selectedAnything	== 1) {
								delta['selected']	+= "," + map3[0] + 
									"_who:" + 
									wave.getPrivateState().get('myColor');
							} else {
								delta['selected']	+= map3[0] + "_who:" + 
									wave.getPrivateState().get('myColor');
							}
							wave.getState().submitDelta(delta);
							selectedAnything	= 1;
						}
					}
				} else if(map3[1]	== "type:attribute") {
					if(posXIniBox <= (parseFloat(coord[0]) - (attributeRadius / 2)) &&
							posYIniBox <= (parseFloat(coord[1]) - (attributeRadius / 2)) &&
							posXBox >= (parseFloat(coord[0]) + (attributeRadius / 2)) &&
							posYBox >= (parseFloat(coord[1]) + (attributeRadius / 2))) {

						if(delta['selected'].indexOf(map3[0])	== -1) {
							if(selectedAnything	== 1) {
								delta['selected']	+= "," + map3[0] + 
									"_who:" + 
									wave.getPrivateState().get('myColor');
							} else {
								delta['selected']	+= map3[0] + "_who:" + 
									wave.getPrivateState().get('myColor');
							}
							wave.getState().submitDelta(delta);
							selectedAnything	= 1;
						}

					}
				}
			}

			if(selectedAnything != 1) {
				if(delta['selected'] != "") {
					delta['selected']	= delta['selected'].substring(
							0,delta['selected'].length-1);
					wave.getState().submitDelta(delta);
				} else {
					delta['selected']	= '0';
					wave.getState().submitDelta(delta);
				}
			}

		} else {
			delta['selected']	= '0';
			wave.getState().submitDelta(delta);
		}
	}
	context.clearRect(0, 0, canvas.width, canvas.height);

}

/*
   Line Functions
 */

/*
 * The startLine function initializes the painting
 * of a line. Since a user can only draw a line
 * between an object and another, the function
 * must check whether the user started painting
 * the line within the boundaries of an object
 * inside the canvas.
 * Beginning: the user wants to draw a line
 * and so an object surrounding the mouse
 * coordinates is looked for.
 * End: If an object was found, it's stored
 * in a variable called firstLineSelection.
 * The mouse's position is stored in posXIni
 * and posYIni.
 */
function startLine() {
	context.clearRect(0, 0, canvas.width, canvas.height);
	if(wave && wave.getState() && 
			wave.getState().get('coordinates') &&
			wave.getState().get('coordinates') != '0' &&
			wave.getState().get('coordinates') != undefined) {

		var coordinatesString	= wave.getState().get ('coordinates');
		var coordinates	= coordinatesString.split(",");
		var selected	= '0';

		if(wave.getState().get('selected') &&
				wave.getState().get('selected') != '0' &&
				wave.getState().get('selected') != undefined) {

			var selected	= wave.getState().get('selected');
		}

		for(x in coordinates) { // this FOR loop cycles through every drawn object

			var map1	= coordinates[x].split("_");
			// map1 contains the id, type, and coordinates of the object
			// where the line starts to be drawn

			var map2	= map1[2].split(":");
			// map2 contains a string 'coord' along with the objects coordinates
			// map2 format: coord,XX-XX

			var mapId	= map1[0].split(":");
			// map3 contains a string 'id' along with the object's id

			var coord	= map2[1].split("-");
			// coord contains only the coordinates' value x,y

			// If the object is an entity
			if(map1[1]	== "type:entity") {

				// If the current mouse's position in X is greater than or equal
				// to the entity's position in X and if the mouse's position
				// in Y is greater than or equal to the entity's position in Y
				if(posX >= coord[0] && posY >= coord[1] &&
						posX <= (parseInt(coord[0]) + entityWidth) &&
						posY <= (parseInt(coord[1]) + entityHeight)) {

					// if the 'selected' state is not null
					if(selected != '0') {

						// if the id in map1 is also in the 'selected' state
						if(selected.indexOf(map1[0])	== -1) {
							firstLineSelection	= "ent:" + mapId[1];
							drawingLine	= 1;
							posXIni	= parseInt(posX);
							posYIni	= parseInt(posY);
							var sel	= {};
							sel.selected	= selected + "," + 
								map1[0] + "_who:" + wave.getPrivateState().get('myColor');
							wave.getState().submitDelta(sel);
						} else {
							selectedString	= map1[0] + '_who:' + wave.getPrivateState().get('myColor');
							if(selected.indexOf(selectedString) != -1) {
								firstLineSelection	= "ent:" + mapId[1];
								drawingLine	= 1;
								posXIni	= parseInt(posX);
								posYIni	= parseInt(posY);
								break;
							}

						}
					} else {
						firstLineSelection	= "ent:" + mapId[1];
						drawingLine	= 1;
						posXIni	= parseInt(posX);
						posYIni	= parseInt(posY);
						var sel	= {};
						sel['selected']	= map1[0] + "_who:" + wave.getPrivateState().get('myColor');
						wave.getState().submitDelta(sel);
					}
					return;

				}

			} else if(map1[1]	== "type:relation") {
				// If the object is a relation

				if(checkInsideRelation(posX,posY,coord[0],coord[1])) {
					if(selected != '0') {
						if(selected.indexOf(map1[0])	== -1) {
							firstLineSelection	= "rel:" + mapId[1];
							drawingLine	= 1;
							posXIni	= parseInt(posX);
							posYIni	= parseInt(posY);

							var sel	= {};
							sel.selected	= selected + "," + 
								map1[0] + "_who:" + wave.getPrivateState().get('myColor');
							wave.getState().submitDelta(sel);
						} else {
							selectedString	= map1[0] + '_who:' + wave.getPrivateState().get('myColor');
							if(selected.indexOf(selectedString) != -1) {
								firstLineSelection	= "rel:" + mapId[1];
								drawingLine	= 1;
								posXIni	= parseInt(posX);
								posYIni	= parseInt(posY);
								break;
							}
						}

					} else {
						firstLineSelection	= "rel:" + mapId[1];
						drawingLine	= 1;
						posXIni	= parseInt(posX);
						posYIni	= parseInt(posY);
						var sel	= {};
						sel['selected']	= map1[0] + "_who:" + wave.getPrivateState().get('myColor');
						wave.getState().submitDelta(sel);
					}
					return;
				}
			} else if(map1[1]	== "type:speGen") {
				//if the object is a spe/gen figure

				if(checkInsideSpeGen(posX,posY,coord[0],coord[1])) {
					if(selected != '0') {
						if(selected.indexOf(map1[0])	== -1) {
							firstLineSelection	= "speGen:" + mapId[1];
							drawingLine	= 1;
							posXIni	= parseInt(posX);
							posYIni	= parseInt(posY);

							var sel	= {};
							sel.selected	= selected + "," + 
								map1[0] + "_who:" + wave.getPrivateState().get('myColor');
							wave.getState().submitDelta(sel);
						} else {
							selectedString	= map1[0] + '_who:' + wave.getPrivateState().get('myColor');
							if(selected.indexOf(selectedString) != -1) {
								firstLineSelection	= "speGen:" + mapId[1];
								drawingLine	= 1;
								posXIni	= parseInt(posX);
								posYIni	= parseInt(posY);
								break;
							}
						}

					} else {
						firstLineSelection	= "speGen:" + mapId[1];
						drawingLine	= 1;
						posXIni	= parseInt(posX);
						posYIni	= parseInt(posY);
						var sel	= {};
						sel['selected']	= map1[0] + "_who:" + wave.getPrivateState().get('myColor');
						wave.getState().submitDelta(sel);
					}
					return;
				}

			}
		}
	}

}

/*
 * the function drawTempLine draws a line dynamically on 
 * the temporal canvas from the beginning of the line
 * to the current mouse position. This process is
 * repeated until the user releases the mouse's button.
 */
function drawTempLine(){

	if(option == '4'){ // normal line
		context.clearRect(0, 0, canvas.width, canvas.height);
		context.beginPath();
		context.moveTo(posXIni,posYIni);
		context.lineTo(posX,posY);
		context.stroke();
		context.closePath();
	}else if(option == '9'){ // double line
		context.clearRect(0, 0, canvas.width, canvas.height);
		var spaceBetweenLines = 20;

		context.beginPath();
		context.moveTo(posXIni,posYIni);
		context.lineTo(posX,posY);
		context.stroke();
		context.closePath();

		var height = Math.abs(posYIni - posY);
		var len = spaceBetweenLines;
		var hypotenuse = Math.sqrt(height*height + len*len);


		//1337	
	}

}

/*
 * The drawLine function ends the drawing of a line,
 * verifying if that line ending is within the boundaries
 * of an object inside the canvas. Also, the function
 * checks that the line isn't from entity to entity,
 * from relation to relation, nor from attribute to attribute.
 * The previous validation also helps prevent "invisible lines"
 * within the canvas, or lines that are drawn within
 * one object only.
 * Beginning: The user finishes drawing a line.
 * End: the new line is appended to the line state, with the
 * firstLineSelection, the initial coordinates, the final
 * coordinates, and the id of the object in which the line ended.
 */
function drawLine() {
	var coordinates	= wave.getState().get('coordinates').split(",");
	var selected	= wave.getState().get('selected');
	var delta	= {};
	var coma	= "";
	delta['line']	= "";

	var selectedAnything	= 0;

	if(wave.getState().get('line') &&
			wave.getState().get('line') != '0' &&
			wave.getState().get('line') != undefined) {

		delta['line']	= wave.getState().get('line');
		coma	= ",";

	}

	// for every object in the canvas that's in the 'coordinates' state
	for(x in coordinates) {

		// coordinates[x] sample: 'id:1293010553606_type:entity_coord:393-54'
		var map1 = coordinates[x].split("_");	// get individual
		// sections of the
		// object's part
		// of the state

		// map1[2] sample: coord:393-54
		var map2 = map1[2].split(":");	// separete 'coord'
		// from 'coord:coordX-coordY'

		// map1[0] sample: id:1293010553606
		var mapId = map1[0].split(":");	// we get the id by taking
		// 'id:' from the part of
		// the state

		// map2[1] sample: 393-54
		var coord = map2[1].split("-");	// individual coordinates

		// if the x object in coordinates[x] is an entity
		if(map1[1] == "type:entity") {

			// if the mouse's X position is greater than or equal to  the
			// entity's X position and if the mouse's Y position is greater
			// than or equal to the entity's Y position and if the mouse's X
			// position is less than or equal to the entity's relative width
			// and if the mouse's Y position is less than or equal to the
			// entity's absolute height... in other words, if the mouse position
			// is inside the entity.
			if(posX >= coord[0] && posY >= coord[1] &&
					posX <= (parseInt(coord[0]) + entityWidth) &&
					posY <= (parseInt(coord[1]) + entityHeight)) {

				// if the line doesn't start at an entity and if 
				// the object where the line starts doesn't have the same id
				// as the x element in coordinates[x]
				if(firstLineSelection.indexOf("ent") == -1
						&& firstLineSelection.indexOf(mapId[1])	== -1) {

					// if the id of the x object in coordinates[x] is not
					// selected
					if(selected.indexOf(map1[0]) == -1) {
						selectedAnything = "ent:" + mapId[1];
						delta['selected'] = selected + "," + map1[0] 
							+ "_who:" + wave.getPrivateState().get('myColor');
						wave.getState().submitDelta(delta);
					} else {
						var selectionString = map1[0] + '_who:' + wave.getPrivateState().get('myColor');
						if(selected.indexOf(selectionString) != -1) {
							selectedAnything = "ent:" + mapId[1];
						}
					}
				}

				break;
			}

			// if the x object in coordinates[x] is a relation
		} else if(map1[1] == "type:relation") {
			if(checkInsideRelation(posX,posY,coord[0],coord[1])) {
				if(firstLineSelection.indexOf(mapId[1])	== -1 && firstLineSelection.indexOf("rel") == -1) {
					if(selected.indexOf(map1[0]) == -1) {
						selectedAnything = "rel:" + mapId[1];
						delta['selected'] = selected + "," + map1[0] 
							+ "_who:" + wave.getPrivateState().get('myColor');
						wave.getState().submitDelta(delta);
					} else {
						var selectArray	= selected.split(",");
						for(y in selectArray) {
							if(selectArray[y].indexOf(map1[0]) != -1 &&
									selectArray[y].indexOf('who:' + wave.getPrivateState().get('myColor')) != -1) {
								selectedAnything = "rel:" + mapId[1];
							}
						}
					}
					break;
				}
			}
		} else if(map1[1] == "type:speGen") {
			if(checkInsideSpeGen(posX,posY,coord[0],coord[1])){
				if(firstLineSelection.indexOf(mapId[1])	== -1 && firstLineSelection.indexOf("speGen") == -1) {
					if(selected.indexOf(map1[0]) == -1) {
						selectedAnything = "speGen:" + mapId[1];
						delta['selected'] = selected + "," + map1[0] 
							+ "_who:" + wave.getPrivateState().get('myColor');
						wave.getState().submitDelta(delta);
					} else {
						var selectArray	= selected.split(",");
						for(y in selectArray) {
							if(selectArray[y].indexOf(map1[0]) != -1 &&
									selectArray[y].indexOf('who:' + wave.getPrivateState().get('myColor')) != -1) {
								selectedAnything = "speGen:" + mapId[1];
							}
						}
					}
					break;
				}
			}
		}
	}

	if(selectedAnything != 0) {
		if(firstLineSelection.indexOf("ent") != -1) { // when the line is drawn from an entity...
			if(selectedAnything.indexOf("rel") != -1) { // ...to a relation
				var map3 = wave.getState().get('relation').split(",");
				var relId = selectedAnything.split(":");
				var entId = firstLineSelection.split(":");

				// for every relation to which the entity is related 
				// when a line is drawn from an entity to a relation
				for(y in map3) {
					if(map3[y].indexOf(relId[1]) != -1) {
						var matches = countMatches(map3[y], "ent:_");
						if(matches != 0) {
							map3[y]	= map3[y].substring(0,map3[y].indexOf("ent:_") + 4)
								+ entId[1] + map3[y].substring(map3[y].indexOf("ent:_") + 4);
							delta['relation'] = map3.join(",");
							break;
						} else {
							var linesBetween = countMatches(map3[y],entId[1]);
							// if there's already 2 lines between a relation and an entity...
							if(linesBetween == 2) {
								drawingLine = 0; // do not connect with a line
								return;
							}

							var wantsRecursive = countMatches(map3[y], firstLineSelection);
							// if the user wants to create a recursive relation and if the
							// relation already has more than one entity related to it...
							if(wantsRecursive && countMatches(map3[y], "ent:") > 1){
								drawingLine = 0; // do not connect with a line
								return;
							}

							var relatedEntities = map3[y].split("_");
							// if it's a recursive relation do not let another entity
							// get connected with that relation
							if(relatedEntities[1] == relatedEntities[3]){
								drawingLine = 0; // do not connect with a line
								return;
							}

							var m3_tmp = map3[y].substring(0, map3[y].indexOf("text:"));
							var idCardlessRel = relId[1];
							insertParticipation(null,idCardlessRel);
							m3_tmp += "ent:" + entId[1] + "_card:null_" ;
							m3_tmp += map3[y].substring(map3[y].indexOf("text:"));
							map3[y]	= m3_tmp;
							delta['relation'] = map3.join(",");
							break;
						}
					}
				}
				var lineType = "";
				if(option == '4'){
					lineType = "_type:normal";
				}else if(option == '9'){
					lineType = "_type:double";
				}
				var id	= wave.getTime();
				delta['line']	+= coma + "id:" + id + "_coordI:" 
					+ posXIni + "-" + posYIni + "_coordF:" + parseInt(posX)
					+ "-" + parseInt(posY) + "_" + selectedAnything + "_" + 
					firstLineSelection + lineType;

				/** Begin of section that creates the 'cotributions' state when a line is added
				  from an entity to an relation **/
				delta['contributions']	= "id:" + id + "_contributor:" + 
					wave.getPrivateState().get('myColor') + "_action:AddObject";
				/** End of section that creates the 'cotributions' state when a line is added 
				  from an entity to a relation**/

				wave.getState().submitDelta(delta);
			} else if(selectedAnything.indexOf("speGen") != -1) { // ...to a spe/gen figure
				var allow = true;
				var speGen = wave.getState().get('speGen');
				var map3 = speGen.split(",");
				var entId = firstLineSelection.split(":");
				var speGenId = selectedAnything.split(":");

				for(y in map3) {
					// if this entity is already linked with a spe/gen figure
					if(map3[y].indexOf(entId[1]) != -1) {
						allow = false;
						break;
					}
					if(map3[y].indexOf(speGenId[1]) != -1){
						var map3Array = map3[y].split("_");
						if(map3Array.length <= 4) { // if the spe/gen figure has no sons
							map3Array.push("son:" + entId[1]);
							map3[y] = map3Array.join("_");
						} else { // if it already has at least one son, append the new one
							var sons = map3Array[4];
							sons += "-" + entId[1];
							map3Array.splice(4,1,sons);
							map3[y] = map3Array.join("_");
						}
						delta['speGen'] = map3.join(",");
						break;
					}
				}	

				if(allow){ // controls if the father can have multiple connections to the spe/gen figure
					var id = wave.getTime();
					delta['line'] += coma + "id:" + id + "_coordI:" 
						+ posXIni + "-" + posYIni + "_coordF:" + parseInt(posX)
						+ "-" + parseInt(posY) + "_" + selectedAnything + "_" + 
						firstLineSelection;
					wave.getState().submitDelta(delta);
				}
			}
		} else if(firstLineSelection.indexOf("rel") != -1) { // when the line is drawn from a relation...
			if(selectedAnything.indexOf("ent") != -1) { // ... to an entity
				var map3 = wave.getState().get('relation').split(",");
				var relId = firstLineSelection.split(":");
				var entId = selectedAnything.split(":");

				for(y in map3) {
					if(map3[y].indexOf(relId[1]) != -1) {
						var matches = countMatches(map3[y], "ent:_");
						if(matches != 0) {
							map3[y]	= map3[y].substring(0,map3[y].indexOf("ent:_") + 4)
								+  entId[1] + map3[y].substring(map3[y].indexOf("ent:_") + 4);
							delta['relation'] = map3.join(",");
							break;

						} else {
							var linesBetween = countMatches(map3[y],entId[1]);
							// if there's already 2 lines between a relation and an entity...
							if(linesBetween == 2) {
								drawingLine = 0; // do not connect with a line
								return;
							}

							var wantsRecursive = countMatches(map3[y], selectedAnything);
							// if the user wants to create a recursive relation and if the
							// relation already has more than one entity related to it...
							if(wantsRecursive && countMatches(map3[y], "ent:") > 1){
								drawingLine = 0; // do not connect with a line
								return;
							}

							var relatedEntities = map3[y].split("_");
							// if it's a recursive relation do not let another entity
							// get connected with that relation
							if(relatedEntities[1] == relatedEntities[3]){
								drawingLine = 0; // do not connect with a line
								return;
							}

							var m3_tmp = map3[y].substring(0, map3[y].indexOf("text:"));
							var idCardlessRel = relId[1];
							insertParticipation(null,idCardlessRel);
							m3_tmp += "ent:" + entId[1] + "_card:null_" ;
							m3_tmp += map3[y].substring(map3[y].indexOf("text:"));
							map3[y]	= m3_tmp;
							delta['relation'] = map3.join(",");
							break;
						}
					}
				}
				var lineType = "";
				if(option == '4'){
					lineType = "_type:normal";
				}else if(option == '9'){
					lineType = "_type:double";
				}
				var id = wave.getTime();
				delta['line'] += coma + "id:" + id + "_coordI:" + 
					posXIni + "-" + posYIni + "_coordF:" + parseInt(posX) + 
					"-" + parseInt(posY) + "_" + firstLineSelection + "_" + 
					selectedAnything + lineType;

				/** Begin of section that creates the 'cotributions' state when a line is added
				  from a relation to an entity **/
				delta['contributions'] = "id:" + id + "_contributor:" + 
					wave.getPrivateState().get('myColor') + "_action:AddObject";
				/** End of section that creates the 'cotributions' state when a line is added 
				  from a relation to an entity **/

				wave.getState().submitDelta(delta);
			}
		} else if(firstLineSelection.indexOf("speGen") != -1) { // when the line is drawn from a speGen figure...
			if(selectedAnything.indexOf("ent") != -1) { // ... to an entity
				var allow = true;
				var speGen = wave.getState().get('speGen');
				var map3 = speGen.split(",");
				var speGenId = firstLineSelection.split(":");
				var entId = selectedAnything.split(":");

				for(y in map3) {
					// if this entity is already linked with a spe/gen figure
					if(map3[y].indexOf(entId[1]) != -1) {
						allow = false;
						break;
					}
					if(map3[y].indexOf(speGenId[1]) != -1){
						var map3Array = map3[y].split("_");
						if(map3Array.length <= 4) { // if the spe/gen figure has no sons
							map3Array.push("son:" + entId[1]);
							map3[y] = map3Array.join("_");
						} else { // if it already has at least one son, append the new one
							var sons = map3Array[4];
							sons += "-" + entId[1];
							map3Array.splice(4,1,sons);
							map3[y] = map3Array.join("_");
						}
						delta['speGen'] = map3.join(",");
						break;
					}
				}	

				if(allow){ // controls if the father can have multiple connections to the spe/gen figure
					var id = wave.getTime();
					delta['line'] += coma + "id:" + id + "_coordI:" 
						+ posXIni + "-" + posYIni + "_coordF:" + parseInt(posX)
						+ "-" + parseInt(posY) + "_" + selectedAnything + "_" + 
						firstLineSelection;
					wave.getState().submitDelta(delta);
				}
			}
		}

		if(hasNewParticipation){	// This if controls if the instructions'
			generalControl('1');	// HTML div changes to 'Ready' or stays with
		}				// 'Insert participation...'
	} 
	context.clearRect(0, 0, canvas.width, canvas.height);
	drawingLine	= 0;
}

/*
   Movement Functions
 */

/*
 * The function startMoving initializes the moving of objects.
 * For this, first, the function gets the object or objects
 * that where selected by the user. Then, the lines that are
 * adjacent to the objects previously mentioned are located, getting
 * at the same time the objects that are located at the edges
 * of the selection so these objects are not moved outside of
 * the canvas' boundaries.
 */
function startMoving() {

	context.clearRect(0, 0, canvas.width, canvas.height);
	if(wave && wave.getState() && wave.getState().get('selected') &&
			wave.getState().get('selected') != '0') {
		if(wave.getState().get('selected').indexOf('who:'+ 
					wave.getPrivateState().get('myColor')) != -1) {

			posXIni	= posX;
			posYIni	= posY;

			var selected	= wave.getState().get('selected').split(",");
			var coordinates	= wave.getState().get('coordinates');

			var selectedByMe	= new Array();
			var linesBeingMoved	= new Array();
			movingLimits	= {};
			objectsBeingMoved	= {};
			movementDelta	= {};
			movingLimits['minX']	= canvasWidth;
			movingLimits['minY']	= canvasHeight;
			movingLimits['maxX']	= 0;
			movingLimits['maxY']	= 0;
			objectsBeingMoved['lines']	= '0';
			linesFlag	= false;

			var count	= 0;

			for(x in selected) {
				if(selected[x].indexOf('who:'+ 
							wave.getPrivateState().get('myColor')) != -1) {

					var idSelectedTemp	= selected[x].split('_');

					// Index of a coma from the point where
					// the id wer'e looking for is located
					// within the coordinates state
					var lastIndex	= coordinates.indexOf(",", 
							coordinates.indexOf(idSelectedTemp[0]));

					if(lastIndex == -1) {
						lastIndex	= coordinates.length;
					}

					selectedByMe[count]	= coordinates.substring(
							coordinates.indexOf(idSelectedTemp[0]),
							lastIndex
							);

					count++;
				}
			}


			count	= 0;
			if(wave.getState().get('line') != '0' && 
					wave.getState().get('line') != null &&
					wave.getState().get('line') != undefined) {

				linesFlag	= true;
				lines	= wave.getState().get('line').split(",");
			}
			for(x in selectedByMe) {
				var sections	= selectedByMe[x].split("_");
				var selectionCoords	= sections[2].substring(
						sections[2].indexOf(":") + 1).split("-");
				var tempId	= sections[0].split(":");
				var tempArray	= new Array();

				if(movingLimits['minX'] > selectionCoords[0]) {
					movingLimits['minX']	= parseInt(selectionCoords[0]);
				}
				if(movingLimits['minY'] > selectionCoords[1]) {
					movingLimits['minY']	= parseInt(selectionCoords[1]);
				}
				if(sections[1]	== "type:entity") {
					if(parseInt(movingLimits['maxX']) + entityWidth < selectionCoords[0]) {
						movingLimits['maxX']	= parseInt(selectionCoords[0]) + entityWidth;
					}
					if(parseInt(movingLimits['maxY']) + entityHeight < selectionCoords[1]) {
						movingLimits['maxY']	= parseInt(selectionCoords[1]) + entityHeight;
					}
				} else if(sections[1]	== "type:relation"){
					if(parseInt(movingLimits['maxX']) + relationWidth < selectionCoords[0]) {
						movingLimits['maxX']	= parseInt(selectionCoords[0]) + relationWidth;
					}
					if(parseInt(movingLimits['maxY']) + relationHeight < selectionCoords[1]) {
						movingLimits['maxY']	= parseInt(selectionCoords[1]) + relationHeight;
					}
				} else if(sections[1]	== "type:attribute"){	
					if(parseInt(movingLimits['maxX']) + (attributeRadius / 2) < selectionCoords[0]) {
						movingLimits['maxX']	= parseInt(selectionCoords[0]) + (attributeRadius / 2);
					}
					if(parseInt(movingLimits['maxY']) + (attributeRadius / 2) < selectionCoords[1]) {
						movingLimits['maxY']	= parseInt(selectionCoords[1]) + (attributeRadius / 2);
					}
				} else if(sections[1]	== "type:speGen"){	
					if(parseInt(movingLimits['maxX']) + (speGenRadius / 2) < selectionCoords[0]) {
						movingLimits['maxX']	= parseInt(selectionCoords[0]) + (speGenRadius / 2);
					}
					if(parseInt(movingLimits['maxY']) + (speGenRadius / 2) < selectionCoords[1]) {
						movingLimits['maxY']	= parseInt(selectionCoords[1]) + (speGenRadius / 2);
					}
				}

				if(linesFlag == true) {
					for(y in lines){
						if(lines[y].indexOf(tempId[1]) != -1) {
							var lineSections	= lines[y].split("_");
							var coordsI	= lineSections[1].substring(
									lineSections[1].indexOf(":") + 1).split("-");
							var coordsF	= lineSections[2].substring(
									lineSections[2].indexOf(":") + 1).split("-");
							if(sections[1]	== "type:relation"){
								if(checkInsideRelation(
											coordsI[0],
											coordsI[1],
											selectionCoords[0],
											selectionCoords[1]
										      )) {
									linesBeingMoved[count]	= lineSections[0] + 
										"_type:line_coord:" + coordsI[0] + 
										"-" + coordsI[1];
									count++;
								} else if(checkInsideRelation(
											coordsF[0],
											coordsF[1],
											selectionCoords[0],
											selectionCoords[1]
											)) {
									linesBeingMoved[count]	= lineSections[0] + 
										"_type:line_coord:" + coordsF[0] + 
										"-" + coordsF[1];
									count++;
								}

							} else if(sections[1]	== "type:entity"){
								if(parseInt(selectionCoords[0]) <= parseInt(coordsI[0]) &&
										parseInt(selectionCoords[1]) <= parseInt(coordsI[1]) &&
										(parseInt(selectionCoords[0]) + entityWidth) >= parseInt(coordsI[0]) &&
										(parseInt(selectionCoords[1]) + entityHeight) >= parseInt(coordsI[1]) ){

									linesBeingMoved[count]	= lineSections[0] + 
										"_type:line_coord:" + coordsI[0] + 
										"-" + coordsI[1];
									count++;

								} else if(parseInt(selectionCoords[0]) <= parseInt(coordsF[0]) &&
										parseInt(selectionCoords[1]) <= parseInt(coordsF[1]) &&
										(parseInt(selectionCoords[0]) + entityWidth) >= parseInt(coordsF[0]) &&
										(parseInt(selectionCoords[1]) + entityHeight) >= parseInt(coordsF[1]) ){

									linesBeingMoved[count]	= lineSections[0] + 
										"_type:line_coord:" + coordsF[0] + 
										"-" + coordsF[1];
									count++;

								} 
							}else if(sections [1]	== "type:attribute") {
								var aI	= Math.pow(parseInt(selectionCoords[0]) - parseInt(coordsI[0]), 2);
								var bI	= Math.pow(parseInt(selectionCoords[1]) - parseInt(coordsI[1]), 2);
								var cI	= Math.sqrt(aI + bI);

								var aF	= Math.pow(parseInt(selectionCoords[0]) - parseInt(coordsF[0]), 2);
								var bF	= Math.pow(parseInt(selectionCoords[1]) - parseInt(coordsF[1]), 2);
								var cF	= Math.sqrt(aF + bF);

								if( cI <= attributeRadius ){

									linesBeingMoved[count]	= lineSections[0] + 
										"_type:line_coord:" + coordsI[0] + 
										"-" + coordsI[1];
									count++;

								} else if( cF <= attributeRadius ){

									linesBeingMoved[count]	= lineSections[0] + 
										"_type:line_coord:" + coordsF[0] + 
										"-" + coordsF[1];
									count++;

								}
							}else if(sections [1]	== "type:speGen") {
								var aI	= Math.pow(parseInt(selectionCoords[0]) - parseInt(coordsI[0]), 2);
								var bI	= Math.pow(parseInt(selectionCoords[1]) - parseInt(coordsI[1]), 2);
								var cI	= Math.sqrt(aI + bI);

								var aF	= Math.pow(parseInt(selectionCoords[0]) - parseInt(coordsF[0]), 2);
								var bF	= Math.pow(parseInt(selectionCoords[1]) - parseInt(coordsF[1]), 2);
								var cF	= Math.sqrt(aF + bF);

								if( cI <= speGenRadius ){

									linesBeingMoved[count]	= lineSections[0] + 
										"_type:line_coord:" + coordsI[0] + 
										"-" + coordsI[1];
									count++;

								} else if( cF <= speGenRadius ){

									linesBeingMoved[count]	= lineSections[0] + 
										"_type:line_coord:" + coordsF[0] + 
										"-" + coordsF[1];
									count++;

								}
							}
						}
					}
				}
			}

			if(linesFlag == false) {
				linesBeingMoved	= '0';
			}

			objectsBeingMoved['objects']	= selectedByMe;
			objectsBeingMoved['lines']	= linesBeingMoved;

			/** Begin of section that creates and uploads the 'cotributions' state when something is moved**/
			var movedObjStr = "";	// String that will contain the objects that were moved, separated
			// by the '-' char.
			var movedLinStr = "";	// String that will contain the lines that were moved, separated
			// by the '-' char.
			for(x in objectsBeingMoved['objects']){
				movedObjStr += objectsBeingMoved['objects'][x].split("_")[0].split(":")[1] + "-";
			}
			for(x in objectsBeingMoved['lines']){
				movedLinStr += objectsBeingMoved['lines'][x].split("_")[0].split(":")[1] + "-";
			}
			movedObjStr = movedObjStr.substring(0,movedObjStr.length-1);
			movedLinStr = movedLinStr.substring(0,movedLinStr.length-1);

			delta2 = {};
			delta2['contributions']	= "";
			if(movedLinStr == undefined || movedLinStr == "undefined"){ // if no lines where moved...
				delta2['contributions']	+= "id:" + movedObjStr + "_contributor:" + 
					wave.getPrivateState().get('myColor') + "_action:MoveObject";
			}else{ // if lines were moved
				//for used to remove duplicates from movedLinStr
				var movedLinStrArr =  movedLinStr.split("-");
				movedLinStr = "";
				for(x in movedLinStrArr){
					if(movedLinStr.indexOf(movedLinStrArr[x]) == -1){
						movedLinStr += movedLinStrArr[x] + '-';
					}
				}
				movedLinStr = movedLinStr.substring(0,movedLinStr.length-1);
				delta2['contributions']	+= "id:" + movedObjStr + "-" + movedLinStr + "_contributor:" + 
					wave.getPrivateState().get('myColor') + "_action:MoveObject";
			}
			wave.getState().submitDelta(delta2);
			/** End of section that creates and uploads the 'cotributions' state when something is moved**/

			movementDelta['x']	= parseInt(posX - posXIni);
			movementDelta['y']	= parseInt(posY - posYIni);
			moving	= 1;
		}
	}

}

/*
 * The whileMoving function paints the objects that are being moved
 * on the temporal canvas. This is done to show the user where the
 * objects will be put when the mouse button is released. Besides this,
 * the function constantly checks that the objects don't go pass
 * the boundaries of the canvas.
 */
function whileMoving() {

	context.clearRect(0, 0, canvas.width, canvas.height);
	movementDelta['x']	= parseInt(posX - posXIni);
	movementDelta['y']	= parseInt(posY - posYIni);

	if(movementDelta['x'] + movingLimits['maxX'] >= canvasWidth - 2) {
		movementDelta['x']	= canvasWidth - movingLimits['maxX'] - 2;
	}

	if(movementDelta['y'] + movingLimits['maxY'] >= canvasHeight - 2) {
		movementDelta['y']	= canvasHeight - movingLimits['maxY'] - 2;
	}

	if(movementDelta['x'] + movingLimits['minX'] <= 2) {
		movementDelta['x']	= (movingLimits['minX'] - 2) * -1;
	}

	if(movementDelta['y'] + movingLimits['minY'] <= 2) {
		movementDelta['y']	= (movingLimits['minY'] - 2) * -1;
	}

	for(x in objectsBeingMoved['objects']) {
		var sections	= objectsBeingMoved['objects'][x].split("_");
		var coord	= sections[2].substring(
				sections[2].indexOf(":") + 1
				).split("-");

		if(sections[1]	== "type:entity"){
			context.strokeRect(
					parseInt(coord[0]) + parseInt(movementDelta['x']),
					parseInt(coord[1]) + parseInt(movementDelta['y']),
					entityWidth,
					entityHeight
					);
		} else if(sections[1]	== "type:relation") {
			var posXTemp	= parseInt(coord[0]) + parseInt(movementDelta['x']);
			var posYTemp	= parseInt(coord[1])  + parseInt(movementDelta['y']);

			context.beginPath();
			context.moveTo(posXTemp, posYTemp + (relationHeight / 2));
			context.lineTo(posXTemp + (relationWidth / 2), posYTemp);
			context.lineTo((posXTemp + relationWidth), 
					posYTemp + (relationHeight / 2));
			context.lineTo(posXTemp + (relationWidth / 2),
					(posYTemp + relationHeight));
			context.closePath();
			context.stroke();
		} else if(sections[1]	== "type:attribute"){
			context.beginPath();
			context.arc(
					parseInt(coord[0]) + parseInt(movementDelta['x']),
					parseInt(coord[1]) + parseInt(movementDelta['y']),
					attributeRadius,
					0,
					Math.PI * 2,
					false);
			context.closePath();
			context.stroke();
		} else if(sections[1]	== "type:speGen"){
			context.beginPath();
			context.arc(
					parseInt(coord[0]) + parseInt(movementDelta['x']),
					parseInt(coord[1]) + parseInt(movementDelta['y']),
					speGenRadius,
					0,
					Math.PI * 2,
					false);
			context.closePath();
			context.stroke();
		}
	}

}

/*
 * The function finishMoving takes the objects that where being
 * moved and updates their coordinates to the new coordinates.
 * This is done by adding the movement delta which defines the
 * differnce in pixels between the old coordinates and the
 * new coordinates.
 */
function finishMoving(){
	var totalDelta	= parseInt(Math.sqrt(Math.pow(Math.sqrt(movementDelta['x']), 2) + Math.pow(Math.sqrt(movementDelta['x']), 2)));
	if( totalDelta < 2 ) {
		moving	= 0;
		return;
	}
	var delta	= {};
	var coordinates	= wave.getState().get('coordinates').split(",");

	for(x in objectsBeingMoved['objects']) {
		for(y in coordinates){
			if(coordinates[y]	== objectsBeingMoved['objects'][x]){
				var sections	= objectsBeingMoved['objects'][x].split("_");
				var coord	= sections[2].substring(
						sections[2].indexOf(":") + 1
						).split("-");
				coordinates[y]	= coordinates[y].substring(0, coordinates[y].indexOf("coord:"))
					+ "coord:" + (parseInt(coord[0]) + movementDelta['x']) + 
					"-" + (parseInt(coord[1]) + movementDelta['y']);
				break;
			}
		}
	}

	delta['coordinates']	= coordinates.join(",");
	wave.getState().submitDelta(delta);

	if(objectsBeingMoved['lines'] != '0') {
		var lines	= wave.getState().get('line').split(",");
		var deltaLine	= {};

		for(x in objectsBeingMoved['lines']) {
			for(y in lines) {
				var lineSections	= lines[y].split("_");
				var selectedLineSections	= objectsBeingMoved['lines'][x].split("_");

				if(lineSections[0]	== selectedLineSections[0]) {
					var coordISections	= lineSections[1].split(":");
					var coordFSections	= lineSections[2].split(":");

					var selectedLineSections	= objectsBeingMoved['lines'][x].split("_");
					var selectedLineCoords	= selectedLineSections[2].split(":");

					var newCoords	= "";

					if(coordISections[1]	== selectedLineCoords[1]) {
						var tempCoords	= coordISections[1].split("-");
						lineSections[1]	= "coordI:" + (parseInt(tempCoords[0]) + 
								movementDelta['x']) + "-" + (parseInt(tempCoords[1]) + 
									movementDelta['y']);
					} else if (coordFSections[1]	== selectedLineCoords[1]) {
						var tempCoords	= coordFSections[1].split("-");
						lineSections[2]	= "coordF:" + (parseInt(tempCoords[0]) + 
								movementDelta['x']) +  "-" + (parseInt(tempCoords[1]) + 
									movementDelta['y']);

					}
					lines[y]	= lineSections.join("_");
					break;
				}
			}
		}

		deltaLine['line']	= lines.join(",");
		wave.getState().submitDelta(deltaLine);
	}
	context.clearRect(0, 0, canvas.width, canvas.height);
}

/*
 * The function resetMoving elements re-initialices the
 * helper variables of the movement tool.
 */
function resetMovingElements() {

	moving	= 0;
	objectsBeingMoved	= null;
	movingLimits	= null;
	movementDelta	= null;

}

/*
 * The function checkParentAttribute returns the id
 * of an attribute's parent attribute.
 */
function checkParentAttribute(attributes, id) {
	var attributes = attributes +  ""; // needed to convert the object into a string
	var attributesArr = attributes.split(",");
	for(var x = 0; x < attributesArr.length; x++) {
		var attributesArrParts = attributesArr[x].split("_");
		if(attributesArrParts[0] == id) {
			var parentsDetails = attributesArrParts[1].split(":");
			if(parentsDetails[0] == "attr") {
				return "id:" + parentsDetails[1];
			}
			return null;
		}
	}
	return null;
}


/*
   Erase Function
 */

/*
 * The function erase is used to erase those elements that have been selected
 * by the user that called te function.
 */
function erase() {
	var erasedElements = "";
	// The above variable is a string that will contain the ids of the
	// erased elements separated by the char '-'

	var delta = {};
	delta['selected'] = wave.getState().get('selected');
	delta['coordinates'] = wave.getState().get('coordinates');
	var attribute = "";
	var entity = "";
	var relation = "";
	var line = "";
	var coordinates	= "";
	var lines = null;
	var coordinatesParts = null;
	var attributesToBeErased = new Array();
	var comesFrom = "";	// Tells what type of object called the function
	// erase(). Used to save some operations from
	// being done when not required.
	var abort = 0;	// Used to control if something is erased.
	// The only reason why this can be 1 is because someone
	// else has selected a child attribute from the parent
	// object.
	/*
	 * The function preEraseAttribute is a local function of the function
	 * erase that takes as a parameter the identificator (id) of the
	 * to-be-erased attribute. It creates an array containing the original
	 * attribute and all of its children attributes. For every element in
	 * that array, the function eraseAttribute is called. It also looks for
	 * selected child attributes. If at least one child attribute is
	 * selected by another user, the erasing of the root attribute will be
	 * aborted. The other input parameter called fromEntRel tells the
	 * function if it was called by an erase action comming from the
	 * functions used to erase entities or relations.
	 */

	function preEraseAttribute(id, fromEntRel){
		var childAttributes = new Array();
		childAttributes = checkChildAttrs(id, false, null);
		var selectedArr = delta['selected'].split(",");

		// this for checks if at least one of the to-be-erased
		// child attributes has been selected by another user
		for(i in selectedArr){
			for(j in childAttributes){
				if(selectedArr[i].split("_")[0] == childAttributes[j]){
					abort = 1;
					break;
				}
			}
		}

		// if none of the to-be-erased child attributes is selected
		// by another user, proceed to erase them
		if(abort == 0){
			childAttributes.push(id);
			for(i in childAttributes){
				if(fromEntRel){
					attributesToBeErased.push(childAttributes[i]);
				}
				eraseAttribute(childAttributes[i]);
			}
		}
	}

	/*
	 * The function eraseAttribute is a local function of the function erase
	 * that takes as a parameter the identificator (id) of the to-be-erased
	 * attribute. The actual deletion of an attribute takes place here. 
	 */
	function eraseAttribute(id) {
		var i;
		var idParts = id.split(":");

		for(i in attributes) {
			if(attributes[i].indexOf(id) != -1) {
				// This condition tells the gadget if the deletion
				// of an attribute was called individually, that is,
				// if it was not called by other means like by
				// calling fisrt the deletion of an entity/relation
				// that has attributes. It comesFrom an attribute
				// when comesFrom equals attr.
				if(comesFrom == "attr") {
					// checks for the attribute's parent attribute
					var parentAttr = checkParentAttribute(attributes, id);

					if(parentAttr != undefined && parentAttr != null) {
						// Checks if the parent attribute has children.
						// The attribute that will be erased in this
						// function will be always contained in what
						// checkChildAttrs returns.
						brothersAndMe = checkChildAttrs(parentAttr, true, attributes);

						// if there's only the id of the attribute that
						// will be erased in what checkChildAttrs
						// returns then call the function to change
						// the parent's attribute state from comp to
						//simple 
						if(brothersAndMe.length == 1) {
							attributesStr = compoundAttributes(null, null, 0, 1, parentAttr, attributes);
							attributes = attributesStr.split(",");
						}
					}
				}
				var attributeParts = attributes[i].split("_");
				var attrId = attributeParts[0].split(":");

				// erases the line that connects the attribute
				// with its father
				eraseLine("attr:" + attrId[1], attributeParts[1]);

				// erases the attributes coordinates from the
				// coordinates' state
				eraseCoordinates(id);

				// adds the attribute's id to the
				// erasedElements' string
				erasedElements += id.split(":")[1] + "-";

				// removes that attribute's entry from the
				// attributes' state
				attributes.splice(i,1);
				break;
			}
		}
	}

	/* 
	 * The function eraseEntity is a local function of the function erase
	 * that takes as a parameter the identificatior (id) of the to-be-erased
	 * entity.
	 * To do this, the function checks the elements that are related to 
	 * this specific entity and then updates them. In case of relations, it
	 * removes the identificator of the section of the state that belongs to
	 * the relation that's vinculated with the to-be-erased entity.
	 * The attributes that belong to the to-be-erased entity will be deleted
	 * too, but before doing so, it's verified if no one else has selected
	 * them. If they are selected the entity will not be erased.
	 * The lines between this entity and the relations to which it is
	 * vinculated will be erased, including the lines that go from the entity
	 * to its attributes.
	 */
	function eraseEntity(id) {
		var j, n;
		for(j in entities) {
			if(entities[j].indexOf(id) != -1) {
				var entId = id.split(":");
				var ownerAttributes = new Array();
				ownerAttributes	= checkChildAttrs("ent:" + entId[1], false, null);
				if(ownerAttributes.length != 0) {
					preEraseAttribute(id, true);
					delta['attribute'] = attributes.join(",");
					if(delta['attribute'] == '') {
						delta['attribute'] = '0';
					}
					attribute = delta['attribute'];
				}
				if(abort == 0){
					if(line != '0') {
						eraseLine("ent:" + entId[1], undefined);
						delta['line'] = lines.join(",");
						if(delta['line'] == '') {
							delta['line'] = '0';
						}
						line = delta['line'];
					}
					eraseCoordinates(id);
					erasedElements += id.split(":")[1] + "-";
					updateRelations("ent:" + entId[1]);
					entities.splice(j, 1);
				}
			}
		}
	}

	/* 
	 * The function eraseSpeGen is a local function of the function erase
	 * that takes as a parameter the identificatior (id) of the to-be-erased
	 * spe/gen figure.
	 * To do this, the function checks the entities that are related to 
	 * this specific spe/gen figure and then updates the lines' state.
	 * The lines between this spe/gen figure and it's entities to which it is
	 * vinculated will be erased.
	 */
	function eraseSpeGen(id) {
		var j, n;
		for(j in speGenFigures) {
			if(speGenFigures[j].indexOf(id) != -1) {
				var speGenId = id.split(":");
				if(line != '0') {
					eraseLine("speGen:" + speGenId[1], undefined);
					delta['line'] = lines.join(",");
					if(delta['line'] == '') {
						delta['line'] = '0';
					}
					line = delta['line'];
				}
				eraseCoordinates(id);
				erasedElements += id.split(":")[1] + "-";
				speGenFigures.splice(j, 1);
			}
		}
	}

	/*
	 * The function eraseRelation is a local function of the function erase.
	 * It takes as a parameter the identificator (id) of the to-be-erased
	 * function. To do this, the function checks if the attributes that are
	 * owned by this relation have been selected by another user. 
	 * If another user has selected them, the erasing of the relation will not 
	 * be done.
	 * It also deletes the lines that are related to this relation and the
	 * lines related to its attributes.
	 */
	function eraseRelation(id) {
		var k, n;
		for(k in relations) {
			if(relations[k].indexOf(id) != -1) {
				var relId = id.split(":");
				var ownerAttributes = new Array();
				ownerAttributes	= checkChildAttrs("rel:" + relId[1], false, null);
				if(ownerAttributes.length != 0) {
					preEraseAttribute(id, true);
					delta['attribute'] = attributes.join(",");
					if(delta['attribute'] == '') {
						delta['attribute'] = '0';
					}
					attribute = delta['attribute'];
				}
				if(abort == 0){
					if(line != '0') {
						eraseLine("rel:" + relId[1], undefined);
						delta['line'] = lines.join(",");
						if(delta['line'] == '') {
							delta['line'] = '0';
						}
						line = delta['line'];
					}
					eraseCoordinates(id);
					erasedElements += id.split(":")[1] + "-";
					relations.splice(k, 1);
				}
			}
		}

	}

	/*
	 * The function eraseLine is a local function of the function erase.
	 * It is called in order to erase lines that are bound to an object.
	 */
	function eraseLine(objeto1, objeto2) {

		var l;
		if(objeto2 != undefined) { 
			for(l in lines) {
				if(lines[l].indexOf(objeto1) != -1 && lines[l].indexOf(objeto2) != -1) {
					erasedElements += lines[l].split(":")[1].split("_")[0] + "-";
					lines.splice(l,1);
					break;
				}
			}
		} else {
			for(l = 0;l < lines.length; l++) {
				if(lines[l].indexOf(objeto1) != -1) {
					erasedElements += lines[l].split(":")[1].split("_")[0] + "-";
					lines.splice(l,1);
					l--;
				}
			}

		}

	}

	/*
	 * The function eraseCoordinates is a local function of the function erase.
	 * It receives as a parameter the identificator (id) of the object that
	 * will have its coordinates erased.
	 */
	function eraseCoordinates(id) {

		var m;
		var idParts	= id.split(":");

		for(m in coordinatesParts) {
			if(coordinatesParts[m].indexOf(id) != -1) {
				coordinatesParts.splice(m,1);
				break;
			}
		}

	}

	/*
	 * The function checkChildAttrs is a local function of the function erase.
	 * It receives as a parameter the identificator (ownerId) of the object
	 * which will have its child attributes erased. The function returns an 
	 * array with the ids of the attributes having ownerId as root.
	 */
	function checkChildAttrs(ownerId, hasState, attributes) {
		if(!hasState) {
			var attributes = wave.getState().get('attribute');
		}
		var attributes = attributes + ""; // converts the object into a string
		var ownerAttributes = new Array();
		if(attributes == null || attributes == undefined || attributes == "" || attributes == '0' || attributes == "null"){
			return ownerAttributes;
		}
		var existingAttr = new Array();
		existingAttr = attributes.split(",");
		ownerId = ownerId.split(":")[1];
		counter = 0;
		var limit = existingAttr.length-1;
		var attrId;
		var target;
		recursiveCheck(ownerId, counter, limit, existingAttr);
		function recursiveCheck(ownerId, counter, limit, existingAttr){
			attrId = existingAttr[counter].split("_")[0].split(":")[1];;
			target = existingAttr[counter].split("_")[1].split(":")[1];;
			if(target == ownerId){
				ownerAttributes.push("id:" + attrId);
				recursiveCheck(attrId, 0, limit, existingAttr);
			}
			if(limit == counter){
				return;
			}
			counter++;
			recursiveCheck(ownerId, counter, limit, existingAttr);
		}

		return ownerAttributes;
	}

	/*
	 * The function updateRelations is a local function of the function erase.
	 * It's called in order to update the section of the corresponding state of
	 * a specific relation. The value of the entity section is updated to 
	 * indicate that this specific entity it's no longer related to this
	 * relation.
	 */
	function updateRelations(entId) {
		updatedRelations = relation.split("_");

		// erases all occurences
		// (recursive relations need this)
		while(updatedRelations.indexOf(entId) > -1){
			erasedElement = updatedRelations.indexOf(entId);
			updatedRelations.splice(erasedElement,1); // erases ent:X
			updatedRelations.splice(erasedElement,1); // erases card:Y
		}

		updatedRelations = updatedRelations.join("_");
		delta['relation'] = updatedRelations;
	}

	if(wave && wave.getState() && delta['selected'] != null &&
			delta['selected'] != undefined && 
			delta['selected'].indexOf('who:' + wave.getPrivateState().get('myColor')) != -1) {

		var myColor = 'who:' + wave.getPrivateState().get('myColor');
		if(wave.getState().get('entity') != null && wave.getState().get('entity') != undefined) {
			entity = wave.getState().get('entity');
		} else {
			entity = "0";
		}

		if(wave.getState().get('relation') != null && wave.getState().get('relation') != undefined) {
			relation = wave.getState().get('relation');
		} else {
			relation = "0";
		}

		if(wave.getState().get('attribute') != null && wave.getState().get('attribute') != undefined) {
			attribute = wave.getState().get('attribute');
		} else {
			attribute = "0";
		}

		if(wave.getState().get('line') != null && wave.getState().get('line') != undefined) {
			line = wave.getState().get('line');
			if(line != '0') {
				lines = line.split(",");
			}
		} else {
			line = "0";
		}

		if(wave.getState().get('coordinates') != null && wave.getState().get('coordinates') != undefined) {
			coordinates = wave.getState().get('coordinates');
			if(coordinates != '0') {
				coordinatesParts = coordinates.split(",");
			}
		} else {
			coordinates = "0";
		}

		if(wave.getState().get('speGen') != null && wave.getState().get('speGen') != undefined) {
			speGen = wave.getState().get('speGen');
			if(speGen != '0') {
				speGenParts = speGen.split(",");
			}
		} else {
			speGen = "0";
		}

		selectedInd = delta['selected'].split(",");

		for(x = 0; x < selectedInd.length; x++) {
			if(selectedInd[x].indexOf(myColor) != -1) {
				var selectedParts = selectedInd[x].split("_");
				if(entity.indexOf(selectedParts[0]) != -1) {
					var entities = new Array();
					var attributes = new Array();
					entities = entity.split(",");
					attributes = attribute.split(",");
					comesFrom = "ent";
					eraseEntity(selectedParts[0]);

					delta['entity']	= entities.join(",");

					if(delta['entity'] == '') {
						delta['entity']	= '0';
					}
					entity = delta['entity'];

					delta['coordinates'] = coordinatesParts.join(",");

					if(delta['coordinates'] == '') {
						delta['coordinates']	= '0';
					}
					coordinates = delta['coordinates'];

					selectedInd.splice(x,1);
					x--;
				} else if(attribute.indexOf(selectedParts[0]) != -1) {
					var attributes = new Array();
					attributes = attribute.split(",");
					comesFrom = "attr";
					preEraseAttribute(selectedParts[0], false);

					delta['attribute'] = attributes.join(",");

					if(delta['attribute'] == '') {
						delta['attribute'] = '0';
					}
					attribute = delta['attribute'];

					delta['line'] = lines.join(",");

					if(delta['line'] == '') {
						delta['line'] = '0';
					}
					line = delta['line'];

					delta['coordinates'] = coordinatesParts.join(",");

					if(delta['coordinates'] == '') {
						delta['coordinates']	= '0';
					}
					coordinates = delta['coordinates'];
					selectedInd.splice(x,1);
					x--;
				} else if(relation.indexOf(selectedParts[0]) != -1) {
					var relations = new Array();
					var attributes = new Array();
					relations = relation.split(",");
					attributes = attribute.split(",");
					comesFrom = "rel";
					eraseRelation(selectedParts[0]);

					delta['relation'] = relations.join(",");

					if(delta['relation'] == '') {
						delta['relation'] = '0';
					}
					relation = delta['relation'];

					delta['coordinates'] = coordinatesParts.join(",");

					if(delta['coordinates'] == '') {
						delta['coordinates'] = '0';
					}
					coordinates = delta['coordinates'];
					selectedInd.splice(x,1);
					x--;
				} else if(speGen.indexOf(selectedParts[0]) != -1) {
					var speGenFigures = new Array();
					speGenFigures = speGen.split(",");
					eraseSpeGen(selectedParts[0]);
					delta['speGen'] = speGenFigures.join(",");

					if(delta['speGen'] == '') {
						delta['speGen'] = '0';
					}
					speGen = delta['speGen'];

					delta['coordinates'] = coordinatesParts.join(",");

					if(delta['coordinates'] == '') {
						delta['coordinates'] = '0';
					}
					coordinates = delta['coordinates'];
					selectedInd.splice(x,1);
					x--;
				}
			}
		}
		if(attributesToBeErased.length != 0) {
			for(x  = 0; x < selectedInd.length; x++) {
				for(y in attributesToBeErased) {
					if(selectedInd.indexOf(attributesToBeErased[y]) != -1) {
						attributesToBeErased.splice(y,1);
						selectedInd.splice(x,1);
						break;
					}
				}
			}
		}

		delta['selected'] = selectedInd.join(",");

		if(delta['selected'] == '') {
			delta['selected'] = '0';
		}
		wave.getState().submitDelta(delta);
	}
	erasedElements = erasedElements.substring(0,erasedElements.length-1);
	/** Begin of section that creates and uploads the 'cotributions' state when something is erased**/
	delta2 = {};
	delta2['contributions']	= "";
	delta2['contributions']	+= erasedElements + "_contributor:" + 
		wave.getPrivateState().get('myColor') + "_action:DeleteObject";
	wave.getState().submitDelta(delta2);
	/** End of section that creates and uploads the 'cotributions' state when something is erased**/

}

/*
   Participate function
 */

/*
 * The participate function is called everytime a user changes the blip into
 * edit mode. If the user has no color assigned, the gadget will assign the
 * the user a color in order for the user to have edition rights.
 */
function participate() {
	if( wave && wave.getState() ) {
		var delta	= {};
		var colorAssigned	= -1;
		delta['participants']	= "";

		if(wave.getState().get('participants') &&
				wave.getState().get('participants') != undefined) {
			delta['participants']	= wave.getState().get('participants') + ",";
		}

		if( delta['participants'] != '0' && 
				delta['participants'] != undefined) {		
			if (delta['participants'].indexOf(wave.getViewer().getId()) == -1 ) {
				delta['participants']	+= "who:" + wave.getViewer().getId() + "_color:";

				for( i in colors ) {
					if( delta['participants'].indexOf(i)	== -1) {
						colorAssigned	= i;
						break;
					}
				}

				delta['participants']	+= colorAssigned;
				canEdit	= 1;
				wave.getState().submitDelta(delta);
				wave.getPrivateState().submitDelta({'myColor':colorAssigned});
			} else {
				canEdit	= 1;
			}
		}

	}

}

/*
   Zoom Functions
 */

/*
 * The zoomIn function.
 */
function zoomIn (){

	if(zoom < 1.8) {
		zoom	= parseFloat(zoom) + 0.1;
		$("#imageView").width(canvasWidth * zoom);
		$("#imageView").height(canvasHeight * zoom);
		$("#imageTemp").width(canvasWidth * zoom);
		$("#imageTemp").height(canvasHeight * zoom);
		$("#textCanvas").width(canvasWidth * zoom);
		$("#textCanvas").height(canvasHeight * zoom);
		wave.getPrivateState().submitDelta({'zoom':zoom});
	}
	context.clearRect(0, 0, canvas.width, canvas.height);

}

/*
 * The zoomOut function
 */
function zoomOut(){

	if(zoom > .2) {
		zoom	= parseFloat(zoom) - 0.1;
		$("#imageView").width(canvasWidth * zoom);
		$("#imageView").height(canvasHeight * zoom);
		$("#imageTemp").width(canvasWidth * zoom);
		$("#imageTemp").height(canvasHeight * zoom);
		$("#textCanvas").width(canvasWidth * zoom);
		$("#textCanvas").height(canvasHeight * zoom);
		wave.getPrivateState().submitDelta({'zoom':zoom});
	}
	context.clearRect(0, 0, canvas.width, canvas.height);

}

/*
   Gadget's Auxiliary Functions
 */

/*
 * The checkInsideRelation function is used to
 * verify if a specific point is inside of the
 * boundaries of a relation. i.e. the boundaries of the
 * diamond that represents the relation.
 */
function checkInsideRelation(mouseX,mouseY,objX,objY) {

	var mouseX = parseInt(mouseX); // The mouse's position on the X axis
	var mouseY = parseInt(mouseY); // The mouse's position on the Y axis
	var objX = parseInt(objX); // The relation's position on the X axis
	var objY = parseInt(objY); // The relation's position on the Y axis				

	if(mouseX < objX) {
		return false;
	}

	if(mouseX > (objX + relationWidth)) {
		return false;
	}

	if(mouseY < objY) {
		return false;
	}

	if(mouseY > (objY + relationHeight)) {
		return false;
	}

	var m1	= (objY - (objY + (relationHeight / 2))) / ((objX + (relationWidth / 2)) - objX);
	var y1	= (m1 * (mouseX - parseInt(objX))) + (parseInt(objY) + (relationHeight / 2));

	if(parseInt(y1) > mouseY) {
		return false;
	}

	var m2	= ((objY + (relationHeight / 2)) - objY) / ((objX + relationWidth) - (objX + (relationWidth / 2)));
	var y2	= (m2 * (mouseX - (parseInt(objX) + (relationWidth / 2)))) + parseInt(objY);

	if(parseInt(y2) > mouseY) {
		return false;
	}

	var m3	= ((objY + relationHeight)-(objY + (relationHeight / 2))) / ((objX + (relationWidth / 2)) - (objX + relationWidth));
	var y3	= (m3 * (mouseX - (parseInt(objX) + relationWidth))) + (parseInt(objY) + (relationHeight / 2));

	if(parseInt(y3) < mouseY) {
		return false;
	}

	var m4	= ((objY + (relationHeight / 2))-(objY + relationHeight)) / (objX - (objX + (relationWidth / 2)));
	var y4	= (m4 * (mouseX - (parseInt(objX) + (relationWidth / 2)))) + (parseInt(objY) + relationHeight);

	if(parseInt(y4) < mouseY) {
		return false;
	}

	return true;

}

/*
 * The checkInsideSpeGen function is used to
 * verify if a specific point is inside the
 * boundaries of a spe/gen figure. i.e. the boundaries of the
 * circle that represents the specialization/generalization.
 */
function checkInsideSpeGen(mouseX,mouseY,objX,objY) {

	var mouseX = parseInt(mouseX); // The mouse's position on the X axis
	var mouseY = parseInt(mouseY); // The mouse's position on the Y axis
	var objX = parseInt(objX); // The spe/gen figure's position on the X axis
	var objY = parseInt(objY); // The spe/gen figure's position on the Y axis

	var distance = Math.pow((objX - mouseX),2) + Math.pow((objY - mouseY),2);
	return (distance <= Math.pow(speGenRadius,2));
}

/*
 * Functions to convert RGB to RGBA
 */
function HexToR(h) {return parseInt((cutHex(h)).substring(0,2),16)}
function HexToG(h) {return parseInt((cutHex(h)).substring(2,4),16)}
function HexToB(h) {return parseInt((cutHex(h)).substring(4,6),16)}
function cutHex(h) {return (h.charAt(0)=="#") ? h.substring(1,7):h}

/*
 * The function countMatches counts the number of occurrences
 * of a certain string within another string.
 */
function countMatches(str, wrd) {

	count	= 0;
	pos	= str.indexOf(wrd);

	while (pos != -1) {
		count++;
		pos	= str.indexOf(wrd, pos + wrd.length)
	}

	return count;

}

/*
 * The function clearDrawing is used to erase the content
 * of the canvas and it's respective states.
 */
function clearDrawing(){

	if(wave && wave.getState()) {
		if(!wave.getState().get('selected') ||
				wave.getState().get('selected')	== '0' ||
				wave.getState().get('selected')	== 'null' ||
				wave.getState().get('selected')	== undefined) {

			context.clearRect(0, 0, canvas.width, canvas.height);
			cleanCanvas();

			var delta = {};
			delta['entity'] = '0';
			delta['coordinates'] = '0';
			delta['selected'] = '0';
			delta['relation'] = '0';
			delta['line'] = '0';
			delta['attribute'] = '0';
			delta['speGen'] = '0';
			delta['contributions']	= "id:null_contributor:" + 
				wave.getPrivateState().get('myColor') +
				"_action:ClearCanvas";

			wave.getState().submitDelta(delta);
		} else {
			indicationsDiv.innerHTML = "For the diagram to be erased, there must not be anything selected";
		}
	}

}


/*
 * The cleanCanvas function cleans the canvas but it doesn't erase the states
 * NOTE = for debbuging use only
 */
function cleanCanvas(){
	contexto.clearRect(0, 0, canvas.width, canvas.height);
	textContext.clearRect(0, 0, canvas.width, canvas.height);
}

/*
   Gadget State Functions
 */

/*
 * The detectMode Function is called by the Mode change event in wave.
 * It detects the mode in which the user is in the Wave. Depending on the mode
 * The Gadget will show different parts of itself and different indications.
 * If The Gadget is in Edit Mode, it will show the buttons that are used
 * to make edition actions in the gadget.
 */
function detectMode() {
	option	= '1';
	drawingLine	= 0;
	context.clearRect(0, 0, canvas.width, canvas.height);

	if(wave.getMode()	== wave.Mode.EDIT) {
		var blanc	= 0;
		if(wave.getState()) {
			if(wave.getState().get('coordinates')){
				if(wave.getState().get('coordinates')	== '0' ||
						wave.getState().get('coordinates')	== null ||
						wave.getState().get('coordinates')	== 'null' ||
						wave.getState().get('coordinates')	== undefined){
					blanc	= 1;
				} else {
					indicationsDiv.innerHTML = "<label>Ready</label>";
					$('#select').removeClass('wave-like-buttons');
					$('#select').addClass('selected-wave-like-buttons');
				}
			} else {
				blanc	= 1;
				indicationsDiv.innerHTML = "<label>Ready</label>";
			}
		} else {
			blanc	= 1;
		}

		$(".edit").show();
		$("#auxMenuDiv").show();
		auxMenuControl();
		participate();
	} else if(wave.getMode()	== wave.Mode.PLAYBACK) {
		$(".edit").hide();
		$('#auxMenuWrapper').hide();
		$("#auxMenuDiv").hide();
		indicationsDiv.innerHTML	= "<label>--Modo Playback--</label>";
	} else if(wave.getMode()	== wave.Mode.VIEW) {
		$(".edit").hide();
		$('#auxMenuWrapper').hide();
		$("#auxMenuDiv").hide();
		indicationsDiv.innerHTML	= "<label>To start drawing here, click on 'Edit' on this blip's upper menu</label>";
	}

}

/*
 * The checkParticipants function is called when a user related action happens
 * within the Wave. It checks the participants that can edit the diagram. 
 * It assigns a color if the user enters EDIT MODE.
 */
function checkParticipants() {
	if(wave.getState() && wave.getState().get('participants') &&
			wave.getState().get('participants') != '0') {

		var participants	= wave.getState().get('participants').split(",");
		var actualParticipants	= wave.getParticipants();
		var stringParticipants	= "";
		for(x in participants) {
			var participantSection	= participants[x].split("_");
			var participantId	= participantSection[0].split(":");
			var participantColor	= participantSection[1].split(":");

			var check	= null;

			for(z in actualParticipants) {
				if(actualParticipants[z].getId()	== participantId[1]) {
					check	= 1;
				}
			}

			if(check	== null || check	== undefined) {
				if(wave.getState().get('selected') &&
						wave.getState().get('selected') != undefined &&
						wave.getState().get('selected') != '0') {

					var selected	= wave.getState().get('selected').split(",");
					var stringSelected	= "";
					for(y in selected) {
						if(selected[y].indexOf('who:' + participantColor[1])	== -1) {
							stringSelected	+= selected[y] + ",";
						}
					}

					if(stringSelected	== "") {
						wave.getState().submitDelta({'selected':'0'});
					} else {
						wave.getState().submitDelta({
								'selected':
								stringSelected.substring(0, stringSelected.length - 1)
								});
					}
				}
			} else {
				stringParticipants	+= participants[x] + ",";
			}
		}

		wave.getState().submitDelta({
				'participants': stringParticipants.substring(0,stringParticipants.length - 1)
				});
	}


}
/*
 * The draw function is called when a user submits a Private or a Shared
 * Delta in Wave. It's purpose is to paint the diagram and make sure
 * every user sees it.
 */
function draw() {
	// The diagram's zoom is updated
	if(wave && wave.getPrivateState()) {
		if(	wave.getPrivateState().get('zoom') && 
				wave.getPrivateState().get('zoom') != undefined) {

			zoom	= parseFloat(wave.getPrivateState().get('zoom'));

		}

		$("#imageView").width(canvasWidth * zoom);
		$("#imageView").height(canvasHeight * zoom);
		$("#imageTemp").width(canvasWidth * zoom);
		$("#imageTemp").height(canvasHeight * zoom);
		$("#textCanvas").width(canvasWidth * zoom);
		$("#textCanvas").height(canvasHeight * zoom);
	}

	if(wave && wave.getState()) {

		// Initial Validations
		if(wave.getMode() == wave.Mode.PLAYBACK){
			indicationsDiv.innerHTML = "<label>--Modo Playback--</label>";
			firstLoaded = 1;
		}


		// If it's the first time that de Gadget loads, the function
		// verifies that the user has permissions to edit the diagram.
		if(firstLoaded){
			if(wave.getState().get('participants') && 
					wave.getState().get('participants') != '0' &&
					wave.getState().get('participants') != undefined &&
					wave.getMode() != wave.Mode.PLAYBACK ) {

				var deltaPart = wave.getState().get('participants');
				if( deltaPart.indexOf(wave.getViewer().getId()) != -1 ) {
					canEdit	= 1;
				}
			}

		}

		cleanCanvas();
		var participants = '0';

		// The user's div is updated with the users' respective color.
		if (wave.getState().get('participants') != null && 
				wave.getState().get('participants') != '0' &&
				wave.getState().get('participants') != undefined) {

			participants = wave.getState().get('participants').split(',');
			usersDiv.innerHTML = "";

			for(x in participants) {
				var participantElements	= participants[x].split('_');
				var temp = participantElements[0].split(':');
				var participantName = wave.getParticipantById("" + temp[1]).getDisplayName();

				temp = null;
				temp = participantElements[1].split(':');
				var colorH = "" + colorsHex[parseInt(temp[1])];
				var color = colors[parseInt(temp[1])];
				var participantHTML = "<p>" + participantName + 
					':<span style= " color: '+colorH + '" >' + 
					color + "</span></p>";
				usersDiv.innerHTML += participantHTML;
			}
		}

		// Drawing of Objects
		if(wave.getState().get('coordinates')){

			var coordinates	= wave.getState().get('coordinates');

			if(coordinates != '0' && coordinates != undefined) {

				var selected	= '0';
				var lines	= '0';
				var mapSelected	= null;

				if(wave.getState().get('selected') &&
						wave.getState().get('selected') != '0' &&
						wave.getState().get('selected') != undefined) {

					selected = wave.getState().get('selected');
					mapSelected = selected.split(",");
				}

				// Drawing of lines
				if(wave.getState().get('line') &&
						wave.getState().get('line') != '0' &&
						wave.getState().get('line') != undefined) {

					lines = wave.getState().get('line').split(",");

					var recBoolean = 0;

					for(ind in lines){
						// sample lines[ind]:
						// id:1292561953659_coordI:756-158_coordF:495-156_rel:1292561948700_ent:1292561944198_type:normal
						mapLine1 = lines[ind].split("_");

						var idAndId = mapLine1[0].split(":");
						var lineId = idAndId[1]; // The line's ID is obtained.

						// This condition checks if the line is between an
						// entity and a relationship, the boolean is used
						// in order to tell the gadget that it must ask for
						// a participation, eg. 1:1, N:N, etc.
						var entityRelation = 0;
						if(mapLine1[3].indexOf('rel') != -1 && mapLine1[4].indexOf('ent') != -1){
							entityRelation = 1;
						}

						mapLine2 = mapLine1[1].split(":");
						mapLine3 = mapLine1[2].split(":");

						lineInit = mapLine2[1].split("-");
						lineEnd	= mapLine3[1].split("-");

						// The line's beggining coordinates in the x,y format
						// are lineInit[0],lineInit[1]
						// The line's ending coordinates in the x,y format
						// are lineEnd[0],lineEnd[1]
						if(lines[ind].indexOf("_type:double") != -1){
							// leftmost line
							contexto.beginPath();
							contexto.moveTo(parseInt(lineInit[0])-4,
									parseInt(lineInit[1]));
							contexto.lineTo(parseInt(lineEnd[0])-4,
									parseInt(lineEnd[1]));
							contexto.stroke();
							contexto.closePath();
							// rightmost line
							contexto.beginPath();
							contexto.moveTo(parseInt(lineInit[0])+4,
									parseInt(lineInit[1]));
							contexto.lineTo(parseInt(lineEnd[0])+4,
									parseInt(lineEnd[1]));
							contexto.stroke();
							contexto.closePath();
							//1337
						}else{
							contexto.beginPath();
							contexto.moveTo(parseInt(lineInit[0]),
									parseInt(lineInit[1]));
							contexto.lineTo(parseInt(lineEnd[0]),
									parseInt(lineEnd[1]));
							contexto.closePath();
							contexto.stroke();
						}

						// If the line is drawn between an entity and a relation
						// then graphically draw the participation card.
						if(entityRelation == 1){

							// mapLine1[3] sample: rel:1292561948700
							var relId = mapLine1[3].split(":");

							// mapLine1[4] sample: ent:1292561944198
							var entId = mapLine1[4].split(":");

							// Endings and beginnings of the line
							var beginningX = parseInt(lineInit[0]);
							var endingX = parseInt(lineEnd[0]);
							var beginningY = parseInt(lineInit[1]);
							var endingY = parseInt(lineEnd[1]);

							// Middle points of the line
							var middleX = (beginningX+endingX)/2;
							var middleY = (beginningY+endingY)/2;

							// The line in lines[ind] connects an entity with a
							// relation, that means that the participation card 
							// must be graphically drawn. 
							// In order to correctly assign the line to its
							// corresponding participation card's number, we
							// must search in the 'relation' state for the
							// elements that the line in lines[ind] connects. 
							// Then, we draw the card's number from that 
							// relation using the middle coordinates.
							var relationsArr = wave.getState().get('relation').split(",");
							var ind2 = 0;
							for(ind2 in relationsArr){
								if(relationsArr[ind2].indexOf(relId[1]) != -1){
									break;
								}
							}
							var relationsArr2 = relationsArr[ind2].split("_");

							var entityRepetitions = 0;
							var ind3 = 0;
							for(ind3 in relationsArr2){
								if(relationsArr2[ind3].indexOf("ent:"+entId[1]) != -1){
									entityRepetitions++;
								}
							}

							if(entityRepetitions != 2){
								var cardIndex = relationsArr2.indexOf("ent:"+entId[1]);
								cardIndex = parseInt(cardIndex);
								cardIndex++;

								// We obtain each card value
								var cardValue = relationsArr2[cardIndex].split(":");

								// If there's no participation...
								if(cardValue[1] == null || cardValue[1] == "" || cardValue[1] == "null"){
									// We draw a small rectangle
									contexto.strokeRect(middleX-1,middleY-14,23,12);
								}else{
									// We draw the card value
									textContext.fillText(cardValue[1], middleX+10, middleY-5);
								}
							}else{
								if(!recBoolean){
									var cardIndex = relationsArr2.indexOf("ent:"+entId[1]);
									cardIndex = parseInt(cardIndex);
									cardIndex++; // add 1 to the index because of the state's structre of a normal relation

									// We obtain each card value
									var cardValue = relationsArr2[cardIndex].split(":");

									// If there's no participation...
									if(cardValue[1] == null || cardValue[1] == "" || cardValue[1] == "null"){
										// We draw a small rectangle
										contexto.strokeRect(middleX-1,middleY-14,23,12);
									}else{
										// We draw the card value
										textContext.fillText(cardValue[1], middleX+10, middleY-5);
									}
									recBoolean = 1;
								}else{
									var cardIndex = relationsArr2.indexOf("ent:"+entId[1]);
									cardIndex = parseInt(cardIndex);
									cardIndex += 3; // add 3 to the index because of the state's structre of a recursive relation

									// We obtain each card value
									var cardValue = relationsArr2[cardIndex].split(":");

									// If there's no participation...
									if(cardValue[1] == null || cardValue[1] == "" || cardValue[1] == "null"){
										// We draw a small rectangle
										contexto.strokeRect(middleX-1,middleY-14,23,12);
									}else{
										// We draw the card value
										textContext.fillText(cardValue[1], middleX+10, middleY-5);
									}
									recBoolean = 0;
								}
							}

						}
					}
				}

				var map1	= wave.getState().get('coordinates').split(",");
				var entities	= wave.getState().get('entity');
				var relations	= wave.getState().get('relation');
				var attributes	= wave.getState().get('attribute');
				var speGen	= wave.getState().get('speGen');
				var line	= wave.getState().get('line');
				var entityArray;
				var relationArray;
				var attributeArray;
				var speGenArray;
				var entityCount	= 0;
				var relationCount = 0;
				var attributeCount = 0;
				var speGenCount = 0;

				if(entities != '0' && entities != null && entities != undefined) {
					entityArray = entities.split(",");
				}

				if(relations != '0' && relations != null && relations != undefined) {
					relationArray = relations.split(",");
				}

				if(attributes != '0' && attributes != null && attributes != undefined) {
					attributeArray = attributes.split(",");
				}

				if(speGen != '0' && speGen != null && speGen != undefined) {
					speGenArray = speGen.split(",");
				}

				// All the elements of the 'coordinates' state are parsed.
				for(var index = 0; index < map1.length; index++) {

					var map2 = map1[index].split("_");
					var colorSelection = '0';

					// The function takes the color of the person that
					// selected the object that's to be painted.
					if(selected != '0' && participants != '0') {
						for(x in mapSelected) {
							var temp = mapSelected[x].split("_");
							if(temp[0] == map2[0]) {
								colorSelection = colorsHex[temp[1].substring(temp[1].indexOf(":") + 1)];
								break;
							}
						}
					}
					if(map2[1] == "type:entity") { // In case the object is an entity...
						var map3 = map2[2].split(":");
						var map4 = map3[1].split("-");
						// entityArray contains the list of entities
						var mapEntityStates = entityArray[entityCount].split("_");
						// entityCount refers to the newest entity
						var textSection	= mapEntityStates[mapEntityStates.length - 1];
						var name = new Array();
						name[0]	= "";
						var nameArrayCount = 0;

						// We look for the entity's name and store it in an
						// array to facilitate using a WordWrap so the text
						// doesn't pass the object's border when it's drawn.
						if(textSection.indexOf(":") != textSection.length - 1) {
							name[0]	= textSection.substring(textSection.indexOf(":") + 1, textSection.length);
							if(name[0].length > 11) {
								var tempName1	= name[0];
								var tempName2	= '';
								for(nameCount	= 0; nameCount < tempName1.length; nameCount++) {
									tempName2	+= tempName1.charAt(nameCount);
									if(nameCount > 11) {
										if((parseFloat(nameCount) % 12.0)	== 0) {
											name[nameArrayCount]	= tempName2;
											nameArrayCount++;
											tempName2	= "";
										}
									}
								}

								if(tempName2 != "") {
									name[nameArrayCount]	= tempName2;
									nameArrayCount++;
									tempName2	= "";
								}
							}
						}

						// A shadow representing a selection of an object is
						// painted. Depending on the variable colorSelection is
						// the color that the shadow will take. This shadow is
						// drawn underneath the actual object.
						if(colorSelection != '0') {
							contexto.save();
							contexto.fillStyle	= 'rgba(' + HexToR(colorSelection) + ',' + 
									HexToG(colorSelection) + ',' + HexToB(colorSelection) + ',' +
									'0.3)';
							contexto.fillRect((parseInt(map4[0]) + 4),
									(parseInt(map4[1]) + 4), entityWidth, entityHeight);
							contexto.restore();
						}

						// The actual drawing of the entities happens here
						// mapEntityStates[1] contains the type of entity
						// If the entity is strong
						if(mapEntityStates[1] == 'type:strong'){
							contexto.save();
							// the next line assigns the entity's fill color
							contexto.fillStyle = colorsHex[mapEntityStates[2].split(":")[1]];
							contexto.fillRect(map4[0],map4[1],entityWidth,entityHeight);
							contexto.strokeRect(map4[0],map4[1],entityWidth,entityHeight);
							contexto.restore();
						}
						// If the entity is weak
						else if(mapEntityStates[1] == 'type:weak'){
							contexto.save();
							// the next line assigns the weak entity's fill color
							contexto.fillStyle = colorsHex[mapEntityStates[2].split(":")[1]];
							// outer rectangle
							contexto.fillRect(map4[0],map4[1],entityWidth,entityHeight);
							contexto.strokeRect(map4[0],map4[1],entityWidth,entityHeight);
							// inner rectangle 
							contexto.fillRect(map4[0]+10,map4[1]+10,entityWidth-20,entityHeight-20);
							contexto.strokeRect(parseInt(map4[0])+10,parseInt(map4[1])+10,entityWidth-20,entityHeight-20);
							contexto.restore();
						}

						// Operations to place the name of the objet in its
						// center.
						if(nameArrayCount > 1) {
							for(x in name) {
								textContext.fillText(name[x], parseInt(map4[0]) + (entityWidth / 2),
										parseInt(map4[1]) + (entityHeight / 2) + ((x - 1) * 12) + 2);
							}
						} else {
							textContext.fillText(name[0], parseInt(map4[0]) + (entityWidth / 2),
									parseInt(map4[1]) + (entityHeight / 2));
						}

						entityCount++;
					} else if(map2[1]	== "type:relation") { // If the object is a relation...
						var map3	= map2[2].split(":");
						var map4	= map3[1].split("-");
						var mapRelationStates	= relationArray[relationCount].split("_");
						var textSection	= mapRelationStates[mapRelationStates.length - 1];
						var name	= new Array();
						name[0]	= "";
						var nameArrayCount	= 0;

						// We look for the relation's name and store it in an
						// array to facilitate using a WordWrap so the text
						// doesn't pass the object's border when it's drawn.
						if(textSection.indexOf(":") != textSection.length - 1) {
							name[0]	= textSection.substring(textSection.indexOf(":") + 1, textSection.length);
							if(name[0].length > 11) {
								var tempName1	= name[0];
								var tempName2	= '';
								for(nameCount	= 0; nameCount < tempName1.length; nameCount++) {
									tempName2	+= tempName1.charAt(nameCount);
									if(nameCount > 11) {
										if((parseFloat(nameCount) % 12.0)	== 0) {
											name[nameArrayCount]	= tempName2;
											nameArrayCount++;
											tempName2	= "";
										}
									}
								}

								if(tempName2 != "") {
									name[nameArrayCount]	= tempName2;
									nameArrayCount++;
									tempName2	= "";
								}
							}
						}

						var rX	= parseInt(map4[0]);	// Position in the X axis
						// of the relation that's
						// to be painted
						var rY	= parseInt(map4[1]);	// Position in the Y axis
						// of the relation that's
						// to be painted

						// A shadow representing a selection of an object is
						// painted. Depending on the variable colorSelection is
						// the color that the shadow will take.
						if(colorSelection != '0'){
							contexto.save();
							contexto.fillStyle	= 'rgba(' + HexToR(colorSelection) + ',' + 
									HexToG(colorSelection) + ',' + HexToB(colorSelection) + ',' +
									'0.3)';
							contexto.beginPath();
							contexto.moveTo(rX + 4,rY + 4 + (relationHeight / 2));
							contexto.lineTo(rX + (relationWidth / 2) + 4, rY + 4);
							contexto.lineTo((rX + relationWidth) + 4, (rY + (relationHeight / 2) + 4));
							contexto.lineTo((rX + (relationWidth / 2) + 4),(rY + relationHeight + 4));
							contexto.closePath();
							contexto.fill();

							contexto.restore();
						}
						if(mapRelationStates[1] == 'type:strong'){
							contexto.save();
							// the next line assigns the relation's fill color
							contexto.fillStyle = colorsHex[mapRelationStates[2].split(":")[1]];
							contexto.beginPath();
							contexto.moveTo(rX,rY + (relationHeight / 2));
							contexto.lineTo(rX + (relationWidth / 2), rY);
							contexto.lineTo((rX + relationWidth), (rY + (relationHeight / 2)));
							contexto.lineTo((rX + (relationWidth / 2)),(rY + relationHeight));
							contexto.closePath();
							contexto.fill();
							contexto.stroke();
							contexto.restore();
							// If the relation is weak
						}else if(mapRelationStates[1] == 'type:weak'){
							contexto.save();
							// the next line assigns the relation's fill color
							contexto.fillStyle = colorsHex[mapRelationStates[2].split(":")[1]];

							//outer romboid
							contexto.beginPath();
							contexto.moveTo(rX,rY + (relationHeight / 2));
							contexto.lineTo(rX + (relationWidth / 2), rY);
							contexto.lineTo((rX + relationWidth), (rY + (relationHeight / 2)));
							contexto.lineTo((rX + (relationWidth / 2)),(rY + relationHeight));
							contexto.closePath();
							contexto.fill();
							contexto.stroke();
							contexto.restore();
							//inner romboid
							contexto.beginPath();
							contexto.moveTo(rX+15,rY + (relationHeight / 2));
							contexto.lineTo(rX + (relationWidth / 2), rY+10);
							contexto.lineTo((rX + relationWidth-15), (rY + (relationHeight / 2)));
							contexto.lineTo((rX + (relationWidth / 2)),(rY + relationHeight-10));
							contexto.closePath();
							contexto.stroke();
							contexto.restore();
						}

						if(nameArrayCount > 1) {
							for(x in name) {
								textContext.fillText(name[x], parseInt(map4[0]) + (relationWidth / 2),
										parseInt(map4[1]) + (relationHeight / 2) + ((x - 1) * 12) + 2);
							}
						} else {
							textContext.fillText(name[0], parseInt(map4[0]) + (relationWidth / 2),
									parseInt(map4[1]) + (relationHeight / 2));
						}

						relationCount++;
					} else if( map2[1]	== "type:attribute" ) { // If the object is an attribute...
						var map3	= map2[2].split(":");
						var map4	= map3[1].split("-"); // object's coordinates
						var mapAttributeStates	= attributeArray[attributeCount].split("_");
						var textSection	= mapAttributeStates[mapAttributeStates.length - 1];
						var name	= new Array();
						var nameArrayCount	= 0;

						// We look for the attribute's name and store it in an
						// array to facilitate using a WordWrap so the text
						// doesn't pass the object's border when it's drawn.
						// Se busca el nombre del atributo y se mete en un
						// arrelgo para facilitar el WordWrap.
						if(textSection.indexOf(":") != textSection.length - 1) {
							name[0]	= textSection.substring(textSection.indexOf(":") + 1, textSection.length);
							if(name[0].length > 11) {
								var tempName1	= name[0];
								var tempName2	= '';
								for(nameCount	= 0; nameCount < tempName1.length; nameCount++) {
									tempName2	+= tempName1.charAt(nameCount);
									if(nameCount > 11) {
										if((parseFloat(nameCount) % 12.0)	== 0) {
											name[nameArrayCount]	= tempName2;
											nameArrayCount++;
											tempName2	= "";
										}
									}
								}

								if(tempName2 != "") {
									name[nameArrayCount]	= tempName2;
									nameArrayCount++;
									tempName2	= "";
								}
							}

						}

						// A shadow representing a selection of an object is
						// painted. Depending on the variable colorSelection is
						// the color that the shadow will take.
						if(colorSelection != '0') {
							contexto.save();	
							contexto.fillStyle = 'rgba(' + HexToR(colorSelection) + ',' + 
									HexToG(colorSelection) + ',' + HexToB(colorSelection) + ',' +
									'0.3)';
							contexto.beginPath();
							contexto.arc(parseInt(map4[0]) + 4, parseInt(map4[1]) + 4, attributeRadius, 0, Math.PI * 2, false);
							contexto.closePath();
							contexto.fill();
							contexto.restore();
						}

						if(mapAttributeStates[2] == "type:simple" || mapAttributeStates[2] == "type:comp"){
							contexto.save();
							// the next line assigns the normal attribute's fill color
							contexto.fillStyle = colorsHex[mapAttributeStates[4].split(":")[1]];
							contexto.beginPath();
							contexto.arc(parseInt(map4[0]), parseInt(map4[1]), attributeRadius, 0, Math.PI * 2, false);
							contexto.closePath();
							contexto.fill();
							contexto.stroke();
							contexto.restore();
						}else if(mapAttributeStates[2] == "type:multi"){
							contexto.save();
							// the next line assigns the multi attribute's fill color
							contexto.fillStyle = colorsHex[mapAttributeStates[4].split(":")[1]];

							// outer circle
							contexto.beginPath();
							contexto.arc(parseInt(map4[0]), parseInt(map4[1]), attributeRadius, 0, Math.PI * 2, false);
							contexto.closePath();
							contexto.fill();
							contexto.stroke();

							// inner circle
							contexto.beginPath();
							contexto.arc(parseInt(map4[0]), parseInt(map4[1]), attributeRadius-3, 0, Math.PI * 2, false);
							contexto.closePath();
							contexto.fill();
							contexto.stroke();
							contexto.restore();
						}else if(mapAttributeStates[2] == "type:derived"){
							contexto.save();
							// the next line assigns the derived attribute's fill color
							contexto.fillStyle = colorsHex[mapAttributeStates[3].split(":")[1]];

							var grain = Math.PI / 7;
							var startAngle = 0;
							var endAngle = startAngle + grain;
							while(endAngle < (Math.PI * 2)){
								contexto.beginPath();
								contexto.arc(parseInt(map4[0]), parseInt(map4[1]), attributeRadius, startAngle, endAngle, false);
								contexto.stroke();
								startAngle = endAngle + grain;
								endAngle = startAngle + grain;
							}
							contexto.closePath();
							contexto.beginPath();
							contexto.arc(parseInt(map4[0]), parseInt(map4[1]), attributeRadius-2, 0, Math.PI * 2, false);
							contexto.closePath();
							contexto.fill();
							contexto.restore();
						}

						ownerObjectId = mapAttributeStates[1].split(":");
						coordinatesPatch = coordinates + ",";
						indexOfOwnerCoordinates = coordinatesPatch.indexOf("coord", coordinatesPatch.indexOf(ownerObjectId[1]));


						// The section "coord:coordinateX-coordinateY" is taken
						// from the "coordinates" state so it can later be
						// separated by the ':' character in an array.
						// After that, the second object of that recently created
						// array, i.e. the coordinates, are taken and separated
						// by the '-' character, having the coordinates X and Y
						// separated in yet another array called 
						// ownerObjectCoordinates.
						ownerObjectCoordinates	= (coordinatesPatch.substring(
									indexOfOwnerCoordinates, coordinatesPatch.indexOf(",", indexOfOwnerCoordinates)
									).split(":"))[1].split("-");

						var namePosition = 0;
						var exactHalf = 0;
						textContext.save();
						if(ownerObjectId[0] == "ent") {
							exactHalf = parseInt(entityWidth / 2);
						} else if(ownerObjectId[0] == "rel") {
							exactHalf = parseInt(relationWidth / 2);
						}
						if((parseInt(ownerObjectCoordinates[0]) + exactHalf) <= parseInt(map4[0])) {
							namePosition = attributeRadius + 2;
							textContext.textAlign	= "left";
						} else if((parseInt(ownerObjectCoordinates[0]) + exactHalf) > parseInt(map4[0])) {
							namePosition = parseInt((attributeRadius * -1) - 2);
							textContext.textAlign	= "right";
						}

						// The key and partial key's text representation, including underscores or underline
						// takes place in the next if/else combo.
						if(nameArrayCount > 1) { // if the name consists of more than one line
							name[0] = name[0].substring(1); // ignore default white space
							textContext.font = "15px monospace";
							for(x in name) {
								textContext.fillText(name[x], parseInt(map4[0]) + namePosition,	parseInt(map4[1]) + ((x - 1) * 12) + 2);
								if(mapAttributeStates[3] == "key:1"){ // key decoration
									contexto.beginPath();
									if(namePosition > 0){ // if the name is to the right
										contexto.moveTo(parseInt(map4[0]) + namePosition,
												parseInt(map4[1]) + ((x - 1) * 12) + 4);
										contexto.lineTo(parseInt(map4[0]) + namePosition + (name[x].length*9.5),
												parseInt(map4[1]) + ((x - 1) * 12) + 4);
									}else{ // if the name is to the left
										contexto.moveTo(parseInt(map4[0]) + namePosition - (name[x].length*9.5),
												parseInt(map4[1]) + ((x - 1) * 12) + 4);
										contexto.lineTo(parseInt(map4[0]) + namePosition,
												parseInt(map4[1]) + ((x - 1) * 12) + 4);
									}
									contexto.closePath();
									contexto.stroke();
								}else if(mapAttributeStates[3] == "key:2"){ // partial key decoration
									var grain = 3.5;
									var control = 0;
									if(namePosition > 0){ // if the name is to the right
										var staticY = parseInt(map4[1]) + ((x - 1) * 12) + 4;
										var startX = parseInt(map4[0]) + namePosition;
										var endX = parseInt(map4[0]) + namePosition + (name[x].length*9.5);
									}else{ // if the name is to the left
										var staticY = parseInt(map4[1]) + ((x - 1) * 12) + 4;
										var startX = parseInt(map4[0]) + namePosition - (name[x].length*9.5);
										var endX = parseInt(map4[0]) + namePosition;
									}
									for(var currentX = startX; currentX <= endX; currentX += grain) {
										if(control % 2 == 0){
											contexto.beginPath();
											contexto.moveTo(currentX,staticY);
											contexto.lineTo(currentX+grain,staticY);
											contexto.stroke();
										}
										control++;
									}
									contexto.closePath();
								}
							}
						} else { // else: the name consists of only one line
							if(name[0] != " "){
								name[0] = name[0].substring(1); // ignore default white space
								textContext.font = "15px monospace";
								textContext.fillText(name[0], parseInt(map4[0]) + namePosition, parseInt(map4[1]) + 4);
								if(mapAttributeStates[3] == "key:1"){ // key decoration
									contexto.beginPath();
									if(namePosition > 0){ // if the name is to the right
										contexto.moveTo(parseInt(map4[0]) + namePosition,
												parseInt(map4[1]) + 6);
										contexto.lineTo(parseInt(map4[0]) + namePosition + (name[0].length*9.5),
												parseInt(map4[1]) + 6);
									}else{ // if the name is to the left
										contexto.moveTo(parseInt(map4[0]) + namePosition - (name[0].length*9.5),
												parseInt(map4[1]) + 6);
										contexto.lineTo(parseInt(map4[0]) + namePosition,
												parseInt(map4[1]) + 6);
									}
									contexto.closePath();
									contexto.stroke();
								}else if(mapAttributeStates[3] == "key:2"){ // prtial key decoration
									var staticY = parseInt(map4[1]) + 6;
									var grain = 3.5;
									var control = 0;
									if(namePosition > 0){ // if the name is to the right
										var startX = parseInt(map4[0]) + namePosition;
										var endX = parseInt(map4[0]) + namePosition + (name[0].length*9.5);
									}else{ // if the name is to the left
										var startX = parseInt(map4[0]) + namePosition - (name[0].length*9.5);
										var endX = parseInt(map4[0]) + namePosition;
									}
									for(var currentX = startX; currentX <= endX; currentX += grain){
										if(control % 2 == 0){
											contexto.beginPath();
											contexto.moveTo(currentX,staticY);
											contexto.lineTo(currentX+grain,staticY);
											contexto.stroke();
										}
										control++;
									}
									contexto.closePath();
								}
							}
						}
						textContext.restore();

						attributeCount++;
					}else if(map2[1] == "type:speGen"){ // if the object is a spe/gen figure
						var map3 = map2[2].split(":");
						var map4 = map3[1].split("-");// x-coordinates at [0], y-coordinates at [1]
						var mapSpeGenStates = speGenArray[speGenCount].split("_");

						// A single letter representing what type of specialization
						// or generalization is being used is drawn in the figure's
						// center.
						if(mapSpeGenStates[1] == "type:0"){ // bi type
						}else if(mapSpeGenStates[1] == "type:1"){ // disjoint type
							textContext.fillText("d", parseInt(map4[0]), parseInt(map4[1]) + 3);
						}else if(mapSpeGenStates[1] == "type:2"){ // overlapping type
							textContext.fillText("o", parseInt(map4[0]), parseInt(map4[1]) + 3);
						}else if(mapSpeGenStates[1] == "type:3"){ // union type
							textContext.fillText("u", parseInt(map4[0]), parseInt(map4[1]) + 3);
						}

						// The arc that goes from the spe/gen figure to its child
						// entities' lines is drawn here.
						var speGenId = map2[0].split(":")[1]; // the spe/gen object
						var fatherId = mapSpeGenStates[3].split(":")[1]; // the spe/gen's father
						var lineArr = line.split(",");
						for(var x in lineArr){
							var lineArrParts = lineArr[x].split("_");
							var obj1Id = lineArrParts[3].split(":")[1]; // object in which the line ends
							var obj2Id = lineArrParts[4].split(":")[1]; // object in which the line begins
							if(fatherId != obj1Id && fatherId != obj2Id){
								if(obj1Id == speGenId || obj2Id == speGenId){
									if(obj1Id == speGenId){ // pick beginning and ending to obtain correct angle
										var beginningX = lineArrParts[2].split(":")[1].split("-")[0];
										var endingX = lineArrParts[1].split(":")[1].split("-")[0];
										var beginningY = lineArrParts[2].split(":")[1].split("-")[1];
										var endingY = lineArrParts[1].split(":")[1].split("-")[1];
									}else if(obj2Id == speGenId){ // pick beginning and ending to obtain correct angle
										var beginningX = lineArrParts[1].split(":")[1].split("-")[0];
										var endingX = lineArrParts[2].split(":")[1].split("-")[0];
										var beginningY = lineArrParts[1].split(":")[1].split("-")[1];
										var endingY = lineArrParts[2].split(":")[1].split("-")[1];
									}
									var middleX = ((parseInt(beginningX) + parseInt(endingX))) /2;
									var middleY = ((parseInt(beginningY) + parseInt(endingY))) /2;
									var speGenXCoord = parseInt(map2[2].split(":")[1].split("-")[0]);
									var speGenYCoord = parseInt(map2[2].split(":")[1].split("-")[1]);
									// attributes of the line that goes from the spe/gen figure
									// to the entity, having the X axis as the point of reference
									var height = endingY - speGenYCoord;
									var len = endingX - speGenXCoord;
									var hypotenuse = Math.sqrt(Math.pow(height,2) + Math.pow(len,2));
									var angle = 0;
									var sinAngle = Math.asin(height / hypotenuse);
									var cosAngle = Math.acos(height / hypotenuse);
									if(speGenXCoord < middleX && speGenYCoord > middleY){ // 1st quadrant
										angle = sinAngle;
									}else if(speGenXCoord > middleX && speGenYCoord > middleY){ // 2nd quadrant
										angle = cosAngle + 90*Math.PI/180;
									}else if(speGenXCoord > middleX && speGenYCoord < middleY){ // 3rd quadrant
										angle = cosAngle + 90*Math.PI/180;
									}else if(speGenXCoord < middleX && speGenYCoord < middleY){ // 4th quadrant
										angle = sinAngle;
									}
									contexto.save();
									contexto.translate(middleX, middleY);
									contexto.rotate(angle);
									contexto.beginPath();
									contexto.arc(0, 0, 20, 3/2*Math.PI, Math.PI/2, false);
									contexto.stroke();
									contexto.closePath();
									contexto.restore();
								}
							}
						}

						// A shadow representing a selection of an object is
						// painted. Depending on the variable colorSelection is
						// the color that the shadow will take.
						if(colorSelection != '0') {
							contexto.save();	
							contexto.fillStyle = 'rgba(' + HexToR(colorSelection) + ',' + 
									HexToG(colorSelection) + ',' + HexToB(colorSelection) + ',' +
									'0.3)';
							contexto.beginPath();
							contexto.arc(parseInt(map4[0]) + 4, parseInt(map4[1]) + 4, speGenRadius, 0, Math.PI * 2, false);
							contexto.closePath();
							contexto.fill();
							contexto.restore();
						}

						contexto.save();
						contexto.fillStyle = colorsHex[mapSpeGenStates[2].split(":")[1]];
						contexto.beginPath();
						contexto.arc(parseInt(map4[0]), parseInt(map4[1]), speGenRadius, 0, Math.PI * 2, false);
						contexto.closePath();
						contexto.fill();
						contexto.stroke();
						contexto.restore();

						speGenCount++;
					}
				}
			} else if(coordinates	== '0') {

				context.clearRect(0, 0, canvas.width, canvas.height);
				cleanCanvas();

			}

		}



		firstLoaded	= 0;
		gadgets.window.adjustHeight();

	}

}
